Changeset 3252

Show
Ignore:
Timestamp:
11/03/07 11:09:17 (16 years ago)
Author:
jerome
Message:

Can now print again under limited circumstances.

Location:
pykota/trunk
Files:
5 modified

Legend:

Unmodified
Added
Removed
  • pykota/trunk/bin/cupspykota

    r3248 r3252  
    44# CUPSPyKota accounting backend 
    55# 
    6 # PyKota - Print Quotas for CUPS and LPRng 
     6# PyKota - Print Quotas for CUPS 
    77# 
    88# (c) 2003, 2004, 2005, 2006, 2007 Jerome Alet <alet@librelogiciel.com> 
     
    4848from pykota.tool import PyKotaTool, PyKotaToolError, crashed 
    4949from pykota.accounter import openAccounter 
    50 # TODO : remove the three lines below and the code which handles 
    51 # TODO : them in a future release. 
    52 from pykota.ipp import IPPRequest as oldIPPRequest 
    53 from pykota.ipp import IPPError as oldIPPError 
    54  
    55 try : 
    56     from pkipplib import pkipplib 
    57 except ImportError :         
    58     haspkipplib = False 
    59 else :     
    60     haspkipplib = True 
     50from pykota import cups 
    6151     
    6252class FakeObject :         
     
    10595        """Sets an attribute whenever SIGTERM is received.""" 
    10696        self.gotSigTerm = 1 
    107         self.printInfo(_("SIGTERM received, job %s cancelled.") % self.JobId) 
     97        self.printInfo(_("SIGTERM received, job %s cancelled.") % self.Ticket.JobId) 
    10898        os.environ["PYKOTASTATUS"] = "CANCELLED" 
    10999         
     
    260250        self.Action = "ALLOW"   # job allowed by default 
    261251        self.Reason = None 
    262         self.JobId = sys.argv[1].strip() 
    263         # use CUPS' user when printing test pages from CUPS' web interface 
    264         self.UserName = sys.argv[2].strip() or self.originalUserName or pwd.getpwuid(os.geteuid())[0] 
    265         self.OriginalUserName = self.UserName[:] 
    266         self.Title = sys.argv[3].strip() 
    267         self.Copies = int(sys.argv[4].strip()) 
    268         self.Options = sys.argv[5].strip() 
     252        try : 
     253            copies = int(sys.argv[4].strip()) 
     254            if copies < 1 : 
     255                raise ValueError 
     256        except (ValueError, TypeError) :     
     257            self.logdebug("Invalid number of copies '%s', using 1 instead." % sys.argv[4]) 
     258            copies = 1 
    269259        if len(sys.argv) == 7 : 
    270             self.InputFile = sys.argv[6] # read job's datas from file 
     260            fname = sys.argv[6] # read job's datas from file 
    271261        else :     
    272             self.InputFile = None        # read job's datas from stdin 
     262            fname = None        # read job's datas from stdin             
     263             
     264        self.Ticket = cups.JobTicket(sys.argv[1].strip(), self.PrinterName, \ 
     265                                     copies, fname, sys.argv[5].strip()) 
     266        self.UserName = self.Ticket.OriginatingUserName                              
     267                                      
    273268        self.DataFile = os.path.join(self.Directory, "%s-%s-%s-%s" % \ 
    274                    (self.myname, self.PrinterName, self.UserName, self.JobId)) 
     269                   (self.myname, self.PrinterName, self.UserName, self.Ticket.JobId)) 
    275270         
    276271        muststartwith = "%s:" % self.myname 
     
    310305        self.DeviceURI = device_uri 
    311306         
    312         connerror = False 
    313         if haspkipplib : 
    314             self.ControlFile = "NotUsedAnymore" 
    315             self.logdebug("Querying CUPS server...") 
    316             cupsserver = pkipplib.CUPS() # TODO : username and password and/or encryption 
    317             answer = cupsserver.getJobAttributes(self.JobId) 
    318             if answer is None : 
    319                 self.printInfo(_("Network error while querying the CUPS server : %s") \ 
    320                                           % cupsserver.lastErrorMessage, "error") 
    321                 connerror = True                         
    322             else :     
    323                 self.logdebug("CUPS server answered without error.") 
    324                 try : 
    325                     john = answer.job["job-originating-host-name"] 
    326                 except KeyError :     
    327                     try : 
    328                         john = answer.operation["job-originating-host-name"] 
    329                     except KeyError :     
    330                         john = (None, None) 
    331                 try :         
    332                     jbing = answer.job["job-billing"] 
    333                 except KeyError :     
    334                     jbing = (None, None) 
    335                      
    336         if connerror or not haspkipplib :         
    337             (ippfilename, ippmessage) = self.parseIPPRequestFile() 
    338             self.ControlFile = ippfilename 
    339             john = ippmessage.operation_attributes.get("job-originating-host-name", \ 
    340                    ippmessage.job_attributes.get("job-originating-host-name", \ 
    341                    (None, None))) 
    342             jbing = ippmessage.job_attributes.get("job-billing", (None, None)) 
    343                  
    344         if type(john) == type([]) :  
    345             john = john[-1] 
    346         (chtype, self.ClientHost) = john  
    347         if type(jbing) == type([]) :  
    348             jbing = jbing[-1] 
    349         (jbtype, self.JobBillingCode) = jbing 
    350         if self.JobBillingCode is None : 
     307        if self.Ticket.BillingCode is None : 
    351308            self.OriginalJobBillingCode = None 
    352309        else :     
    353             self.JobBillingCode = self.UTF8ToUserCharset(self.JobBillingCode) 
    354             self.OriginalJobBillingCode = self.JobBillingCode[:] 
     310            self.Ticket.BillingCode = self.UTF8ToUserCharset(self.Ticket.BillingCode) 
     311            self.OriginalJobBillingCode = self.Ticket.BillingCode[:] 
    355312             
    356313        baselockfilename = self.DeviceURI.replace("/", ".") 
     
    365322        self.logdebug("Printername : %s" % self.PrinterName) 
    366323        self.logdebug("Username : %s" % self.UserName) 
    367         self.logdebug("JobId : %s" % self.JobId) 
    368         self.logdebug("Title : %s" % self.Title) 
    369         self.logdebug("Filename : %s" % self.InputFile) 
    370         self.logdebug("Copies : %s" % self.Copies) 
    371         self.logdebug("Options : %s" % self.Options) 
     324        self.logdebug("JobId : %s" % self.Ticket.JobId) 
     325        self.logdebug("Title : %s" % self.Ticket.Title) 
     326        self.logdebug("Filename : %s" % self.Ticket.FileName) 
     327        self.logdebug("Copies : %s" % self.Ticket.Copies) 
     328        self.logdebug("Options : %s" % self.Ticket.Options) 
    372329        self.logdebug("Directory : %s" % self.Directory)  
    373330        self.logdebug("DataFile : %s" % self.DataFile) 
    374         self.logdebug("ControlFile : %s" % self.ControlFile) 
    375         self.logdebug("JobBillingCode : %s" % self.JobBillingCode) 
    376         self.logdebug("JobOriginatingHostName : %s" % self.ClientHost) 
     331        self.logdebug("JobBillingCode : %s" % self.Ticket.BillingCode) 
     332        self.logdebug("JobOriginatingHostName : %s" % self.Ticket.OriginatingHostName) 
    377333         
    378334        # fakes some entries to allow for external mailto 
     
    413369        stripprefix = self.config.getStripTitle(self.PrinterName) 
    414370        if stripprefix : 
    415             if fnmatch.fnmatch(self.Title[:len(stripprefix)], stripprefix) : 
     371            if fnmatch.fnmatch(self.Ticket.Title[:len(stripprefix)], stripprefix) : 
    416372                self.logdebug("Prefix [%s] removed from job's title [%s]." \ 
    417                                       % (stripprefix, self.Title)) 
    418                 self.Title = self.Title[len(stripprefix):] 
     373                                      % (stripprefix, self.Ticket.Title)) 
     374                self.Ticket.Title = self.Ticket.Title[len(stripprefix):] 
    419375                 
    420376        self.logdebug("Username : %s" % self.UserName) 
    421         self.logdebug("BillingCode : %s" % self.JobBillingCode) 
    422         self.logdebug("Title : %s" % self.Title) 
     377        self.logdebug("BillingCode : %s" % self.Ticket.BillingCode) 
     378        self.logdebug("Title : %s" % self.Ticket.Title) 
    423379        self.logdebug("Job's attributes sanitizing done.") 
    424380                 
     
    495451                    self.UserName = username 
    496452                if billingcode is not None : 
    497                     self.JobBillingCode = billingcode  
     453                    self.Ticket.BillingCode = billingcode  
    498454        self.logdebug("Job ticket overwriting done.") 
    499455             
     
    503459        mustclose = 0 
    504460        outfile = open(self.DataFile, "wb")     
    505         if self.InputFile is not None : 
     461        if self.Ticket.FileName is not None : 
    506462            self.regainPriv() 
    507             infile = open(self.InputFile, "rb") 
    508             self.logdebug("Reading input datas from %s" % self.InputFile) 
     463            infile = open(self.Ticket.FileName, "rb") 
     464            self.logdebug("Reading input datas from %s" % self.Ticket.FileName) 
    509465            mustclose = 1 
    510466        else :     
     
    584540                                   % self.softwareJobPrice) 
    585541         
    586     def getCupsConfigDirectives(self, directives=[]) : 
    587         """Retrieves some CUPS directives from its configuration file. 
    588          
    589            Returns a mapping with lowercased directives as keys and  
    590            their setting as values. 
    591         """ 
    592         self.logdebug("Parsing CUPS' configuration file...") 
    593         dirvalues = {}  
    594         cupsroot = os.environ.get("CUPS_SERVERROOT", "/etc/cups") 
    595         cupsdconf = os.path.join(cupsroot, "cupsd.conf") 
    596         try : 
    597             conffile = open(cupsdconf, "r") 
    598         except IOError :     
    599             raise PyKotaToolError, "Unable to open %s" % cupsdconf 
    600         else :     
    601             for line in conffile.readlines() : 
    602                 linecopy = line.strip().lower() 
    603                 for di in [d.lower() for d in directives] : 
    604                     if linecopy.startswith("%s " % di) : 
    605                         try : 
    606                             val = line.split()[1] 
    607                         except :     
    608                             pass # ignore errors, we take the last value in any case. 
    609                         else :     
    610                             dirvalues[di] = val 
    611             conffile.close()             
    612         self.logdebug("CUPS' configuration file parsed successfully.") 
    613         return dirvalues        
    614              
    615     def parseIPPRequestFile(self) :         
    616         """Parses the IPP message file and returns a tuple (filename, parsedvalue).""" 
    617         self.logdebug("Parsing IPP request file...") 
    618          
    619         class DummyClass : 
    620             """Class used to avoid errors.""" 
    621             operation_attributes = {} 
    622             job_attributes = {} 
    623              
    624         ippmessage = DummyClass() # in case the code below fails 
    625          
    626         self.regainPriv() 
    627         cupsdconf = self.getCupsConfigDirectives(["RequestRoot"]) 
    628         requestroot = cupsdconf.get("requestroot", "/var/spool/cups") 
    629         if (len(self.JobId) < 5) and self.JobId.isdigit() : 
    630             ippmessagefile = "c%05i" % int(self.JobId) 
    631         else :     
    632             ippmessagefile = "c%s" % self.JobId 
    633         ippmessagefile = os.path.join(requestroot, ippmessagefile) 
    634         try : 
    635             ippdatafile = open(ippmessagefile) 
    636         except :     
    637             self.logdebug("Unable to open IPP request file %s" % ippmessagefile) 
    638         else :     
    639             self.logdebug("Parsing of IPP request file %s begins." % ippmessagefile) 
    640             try : 
    641                 ippmessage = oldIPPRequest(ippdatafile.read()) 
    642                 ippmessage.parse() 
    643             except oldIPPError, msg :     
    644                 self.printInfo("Error while parsing %s : %s" \ 
    645                                       % (ippmessagefile, msg), "warn") 
    646             else :     
    647                 self.logdebug("Parsing of IPP request file %s ends." \ 
    648                                        % ippmessagefile) 
    649             ippdatafile.close() 
    650         self.dropPriv() 
    651         self.logdebug("IPP request file parsed successfully.") 
    652         return (ippmessagefile, ippmessage) 
    653                  
    654542    def exportJobInfo(self) :     
    655543        """Exports the actual job's attributes to the environment.""" 
     
    661549        os.environ["PYKOTAJOBSIZEBYTES"] = str(self.JobSizeBytes) 
    662550        os.environ["PYKOTAMD5SUM"] = self.JobMD5Sum 
    663         os.environ["PYKOTAJOBORIGINATINGHOSTNAME"] = self.ClientHost or "" 
    664         os.environ["PYKOTAJOBID"] = self.JobId 
     551        os.environ["PYKOTAJOBORIGINATINGHOSTNAME"] = self.Ticket.OriginatingHostName or "" 
     552        os.environ["PYKOTAJOBID"] = self.Ticket.JobId 
    665553        os.environ["PYKOTAUSERNAME"] = self.UserName 
    666         os.environ["PYKOTAORIGINALUSERNAME"] = self.OriginalUserName 
    667         os.environ["PYKOTATITLE"] = self.Title 
    668         os.environ["PYKOTACOPIES"] = str(self.Copies) 
    669         os.environ["PYKOTAOPTIONS"] = self.Options 
    670         os.environ["PYKOTAFILENAME"] = self.InputFile or "" 
    671         os.environ["PYKOTAJOBBILLING"] = self.JobBillingCode or "" 
     554        os.environ["PYKOTAORIGINALUSERNAME"] = self.Ticket.OriginalUserName 
     555        os.environ["PYKOTATITLE"] = self.Ticket.Title 
     556        os.environ["PYKOTACOPIES"] = str(self.Ticket.Copies) 
     557        os.environ["PYKOTAOPTIONS"] = self.Ticket.Options 
     558        os.environ["PYKOTAFILENAME"] = self.Ticket.FileName or "" 
     559        os.environ["PYKOTAJOBBILLING"] = self.Ticket.BillingCode or "" 
    672560        os.environ["PYKOTAORIGINALJOBBILLING"] = self.OriginalJobBillingCode or "" 
    673         os.environ["PYKOTACONTROLFILE"] = self.ControlFile 
    674561        os.environ["PYKOTAPRINTERHOSTNAME"] = self.PrinterHostName 
    675562        os.environ["PYKOTAPRECOMPUTEDJOBSIZE"] = str(self.softwareJobSize) 
     
    763650            return "%s@%s(%s) => %s" % (self.UserName, \ 
    764651                                        self.PrinterName, \ 
    765                                         self.JobId, \ 
     652                                        self.Ticket.JobId, \ 
    766653                                        message) 
    767654        except :                                                
     
    970857        self.logdebug("Retrieving billing code information from the database...") 
    971858        self.BillingCode = None 
    972         if self.JobBillingCode : 
    973             self.BillingCode = self.storage.getBillingCode(self.JobBillingCode) 
     859        if self.Ticket.BillingCode : 
     860            self.BillingCode = self.storage.getBillingCode(self.Ticket.BillingCode) 
    974861            if self.BillingCode.Exists : 
    975                 self.logdebug("Billing code [%s] found in database." % self.JobBillingCode) 
     862                self.logdebug("Billing code [%s] found in database." % self.Ticket.BillingCode) 
    976863            else : 
    977                 msg = "Unknown billing code [%s] : " % self.JobBillingCode 
     864                msg = "Unknown billing code [%s] : " % self.Ticket.BillingCode 
    978865                (newaction, script) = self.config.getUnknownBillingCode(self.PrinterName) 
    979866                if newaction == "CREATE" : 
    980867                    self.logdebug(msg + "will be created.") 
    981868                    self.storage.addBillingCode(self.BillingCode) 
    982                     self.BillingCode = self.storage.getBillingCode(self.JobBillingCode) 
     869                    self.BillingCode = self.storage.getBillingCode(self.Ticket.BillingCode) 
    983870                    if self.BillingCode.Exists : 
    984871                        self.logdebug(msg + "has been created.") 
     
    13111198             
    13121199            # adds the current job to history     
    1313             self.Printer.addJobToHistory(self.JobId, self.User, self.accounter.getLastPageCounter(), \ 
    1314                                     self.Action, self.JobSize, self.JobPrice, self.InputFile, \ 
    1315                                     self.Title, self.Copies, self.Options, self.ClientHost, \ 
    1316                                     self.JobSizeBytes, self.JobMD5Sum, None, self.JobBillingCode, \ 
     1200            self.Printer.addJobToHistory(self.Ticket.JobId, self.User, self.accounter.getLastPageCounter(), \ 
     1201                                    self.Action, self.JobSize, self.JobPrice, self.Ticket.FileName, \ 
     1202                                    self.Ticket.Title, self.Ticket.Copies, self.Ticket.Options, self.Ticket.OriginatingHostName, \ 
     1203                                    self.JobSizeBytes, self.JobMD5Sum, None, self.Ticket.BillingCode, \ 
    13171204                                    self.softwareJobSize, self.softwareJobPrice) 
    13181205            self.printInfo(_("Job added to history.")) 
     
    13631250        loopcnt = 1  
    13641251        while True :             
    1365             if self.InputFile is None : 
     1252            if self.Ticket.FileName is None : 
    13661253                infile = open(self.DataFile, "rb") 
    13671254            else :     
    13681255                infile = None 
    13691256            retcode = self.runOriginalBackend(infile) 
    1370             if self.InputFile is None : 
     1257            if self.Ticket.FileName is None : 
    13711258                infile.close() 
    13721259            if not retcode : 
     
    14741361                retcode = wrapper.mainWork() 
    14751362            except KeyboardInterrupt :     
    1476                 wrapper.printInfo(_("Job %s interrupted by the administrator !") % wrapper.JobId, "warn") 
     1363                wrapper.printInfo(_("Job %s interrupted by the administrator !") % wrapper.Ticket.JobId, "warn") 
    14771364                retcode = 0 
    14781365            except SystemExit, err :     
  • pykota/trunk/conf/pykota.conf.sample

    r3242 r3252  
    986986# PYKOTAFILENAME : The name of the file which contains the job's datas or  
    987987#                  empty if datas come from stdin 
    988 # PYKOTACONTROLFILE : The name of the IPP message file 
    989988# PYKOTAMD5SUM : Contains an hexadecimal digest of the md5 sum of the job's datas 
    990989# PYKOTAPHASE : BEFORE or AFTER the job is sent to the printer 
  • pykota/trunk/pykota/accounters/ink.py

    r3245 r3252  
    8888                    jobsize = len(pages) 
    8989                    try : 
    90                         if self.filter.InputFile is not None : 
     90                        if self.filter.Ticket.FileName is not None : 
    9191                            # when a filename is passed as an argument, the backend  
    9292                            # must generate the correct number of copies. 
    93                             jobsize *= self.filter.Copies 
    94                             self.inkUsage *= self.filter.Copies 
     93                            jobsize *= self.filter.Ticket.Copies 
     94                            self.inkUsage *= self.filter.Ticket.Copies 
    9595                    except AttributeError : # When not run from the cupspykota backend  
    9696                        pass 
  • pykota/trunk/pykota/accounters/software.py

    r3245 r3252  
    6767                else :     
    6868                    try : 
    69                         if self.filter.InputFile is not None : 
     69                        if self.filter.Ticket.FileName is not None : 
    7070                            # when a filename is passed as an argument, the backend  
    7171                            # must generate the correct number of copies. 
    72                             jobsize *= self.filter.Copies 
     72                            jobsize *= self.filter.Ticket.Copies 
    7373                    except AttributeError : # When not run from the cupspykota backend         
    7474                        pass 
     
    128128             
    129129        pagecounter = pagecounter or 0     
    130         if self.filter.InputFile is not None : 
     130        if self.filter.Ticket.FileName is not None : 
    131131            # when a filename is passed as an argument, the backend  
    132132            # must generate the correct number of copies. 
    133             pagecounter *= self.filter.Copies 
     133            pagecounter *= self.filter.Ticket.Copies 
    134134                         
    135135        return pagecounter 
  • pykota/trunk/pykota/cups.py

    r3249 r3252  
    3030    raise RuntimeError, "The python-pkipplib module is now mandatory. You can download pkipplib from http://www.pykota.com/" 
    3131 
    32 class Job :     
     32class JobTicket :     
    3333    """A class to hold CUPS print job informations.""" 
    34     def __init__(self, jobid=None, copies=1, filename=None) : 
     34    def __init__(self, jobid=None, printername=None, copies=1, filename=None, \ 
     35                       options=None) : 
    3536        """Initializes a print job's information.""" 
    3637        self.JobId = jobid 
     38        self.PrinterName = printername 
    3739        self.Copies = copies 
    3840        self.FileName = filename 
     41        self.Options = options 
    3942        self.Charset = None 
    40         self.UserName = None 
     43        self.OriginatingUserName = None 
     44        self.OriginalUserName = None 
    4145        self.Title = None 
    4246        self.BillingCode = None 
     
    6165    def retrieveAttributesFromCUPS(self) : 
    6266        """Retrieve attribute's values from CUPS.""" 
     67        import os 
     68        f = open("/tmp/debug", "w") 
     69        f.write("%s\n" % os.environ.get("CUPS_SERVER", "")) 
     70        f.close() 
    6371        server = pkipplib.CUPS() # TODO : username and password and/or encryption 
    6472        answer = server.getJobAttributes(self.JobId) 
     
    6674            raise PyKotaToolError, "Network error while querying the CUPS server : %s" \ 
    6775                                      % server.lastErrorMessage 
    68         (dummy, self.Charset) = self.getAttributeTypeAndValue(answer, "attributes-charset", "operation")                               
    69         (dummy, self.UserName) = self.getAttributeTypeAndValue(answer, "job-originating-user-name") 
     76        (dummy, self.Charset) = self.getAttributeTypeAndValue(answer, "attributes-charset", "operation") 
     77        (dummy, self.OriginatingUserName) = self.getAttributeTypeAndValue(answer, "job-originating-user-name") 
    7078        (dummy, self.Title) = self.getAttributeTypeAndValue(answer, "job-name") 
    7179        (dummy, self.BillingCode) = self.getAttributeTypeAndValue(answer, "job-billing") 
     
    7583        (dummy, self.TimeAtProcessing) = self.getAttributeTypeAndValue(answer, "time-at-processing") 
    7684        (dummy, self.MimeType) = self.getAttributeTypeAndValue(answer, "document-format") 
     85        self.OriginalUserName = self.OriginatingUserName[:] 
    7786     
    7887if __name__ == "__main__" :     
     
    8190        sys.stderr.write("usage : python cups.py jobid\n") 
    8291    else :     
    83         job = Job(int(sys.argv[1])) 
    84         for attribute in ("Charset", "JobId", "Copies", "FileName", "UserName",  
     92        job = JobTicket(int(sys.argv[1])) 
     93        for attribute in ("Charset", "JobId", "Copies", "FileName", "OriginatingUserName",  
    8594                          "Title", "BillingCode", "OriginatingHostName",  
    8695                          "TimeAtCreation", "TimeAtProcessing", "UUID",