Changeset 3252
- Timestamp:
- 11/03/07 11:09:17 (17 years ago)
- Location:
- pykota/trunk
- Files:
-
- 5 modified
Legend:
- Unmodified
- Added
- Removed
-
pykota/trunk/bin/cupspykota
r3248 r3252 4 4 # CUPSPyKota accounting backend 5 5 # 6 # PyKota - Print Quotas for CUPS and LPRng6 # PyKota - Print Quotas for CUPS 7 7 # 8 8 # (c) 2003, 2004, 2005, 2006, 2007 Jerome Alet <alet@librelogiciel.com> … … 48 48 from pykota.tool import PyKotaTool, PyKotaToolError, crashed 49 49 from 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 50 from pykota import cups 61 51 62 52 class FakeObject : … … 105 95 """Sets an attribute whenever SIGTERM is received.""" 106 96 self.gotSigTerm = 1 107 self.printInfo(_("SIGTERM received, job %s cancelled.") % self. JobId)97 self.printInfo(_("SIGTERM received, job %s cancelled.") % self.Ticket.JobId) 108 98 os.environ["PYKOTASTATUS"] = "CANCELLED" 109 99 … … 260 250 self.Action = "ALLOW" # job allowed by default 261 251 self.Reason = None 262 self.JobId = sys.argv[1].strip()263 # use CUPS' user when printing test pages from CUPS' web interface264 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 269 259 if len(sys.argv) == 7 : 270 self.InputFile = sys.argv[6] # read job's datas from file260 fname = sys.argv[6] # read job's datas from file 271 261 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 273 268 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)) 275 270 276 271 muststartwith = "%s:" % self.myname … … 310 305 self.DeviceURI = device_uri 311 306 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 : 351 308 self.OriginalJobBillingCode = None 352 309 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[:] 355 312 356 313 baselockfilename = self.DeviceURI.replace("/", ".") … … 365 322 self.logdebug("Printername : %s" % self.PrinterName) 366 323 self.logdebug("Username : %s" % self.UserName) 367 self.logdebug("JobId : %s" % self. JobId)368 self.logdebug("Title : %s" % self.Ti tle)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) 372 329 self.logdebug("Directory : %s" % self.Directory) 373 330 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) 377 333 378 334 # fakes some entries to allow for external mailto … … 413 369 stripprefix = self.config.getStripTitle(self.PrinterName) 414 370 if stripprefix : 415 if fnmatch.fnmatch(self.Ti tle[:len(stripprefix)], stripprefix) :371 if fnmatch.fnmatch(self.Ticket.Title[:len(stripprefix)], stripprefix) : 416 372 self.logdebug("Prefix [%s] removed from job's title [%s]." \ 417 % (stripprefix, self.Ti tle))418 self.Ti tle = self.Title[len(stripprefix):]373 % (stripprefix, self.Ticket.Title)) 374 self.Ticket.Title = self.Ticket.Title[len(stripprefix):] 419 375 420 376 self.logdebug("Username : %s" % self.UserName) 421 self.logdebug("BillingCode : %s" % self. JobBillingCode)422 self.logdebug("Title : %s" % self.Ti tle)377 self.logdebug("BillingCode : %s" % self.Ticket.BillingCode) 378 self.logdebug("Title : %s" % self.Ticket.Title) 423 379 self.logdebug("Job's attributes sanitizing done.") 424 380 … … 495 451 self.UserName = username 496 452 if billingcode is not None : 497 self. JobBillingCode = billingcode453 self.Ticket.BillingCode = billingcode 498 454 self.logdebug("Job ticket overwriting done.") 499 455 … … 503 459 mustclose = 0 504 460 outfile = open(self.DataFile, "wb") 505 if self. InputFile is not None :461 if self.Ticket.FileName is not None : 506 462 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) 509 465 mustclose = 1 510 466 else : … … 584 540 % self.softwareJobPrice) 585 541 586 def getCupsConfigDirectives(self, directives=[]) :587 """Retrieves some CUPS directives from its configuration file.588 589 Returns a mapping with lowercased directives as keys and590 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" % cupsdconf600 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] = val611 conffile.close()612 self.logdebug("CUPS' configuration file parsed successfully.")613 return dirvalues614 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 fails625 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.JobId633 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 654 542 def exportJobInfo(self) : 655 543 """Exports the actual job's attributes to the environment.""" … … 661 549 os.environ["PYKOTAJOBSIZEBYTES"] = str(self.JobSizeBytes) 662 550 os.environ["PYKOTAMD5SUM"] = self.JobMD5Sum 663 os.environ["PYKOTAJOBORIGINATINGHOSTNAME"] = self. ClientHostor ""664 os.environ["PYKOTAJOBID"] = self. JobId551 os.environ["PYKOTAJOBORIGINATINGHOSTNAME"] = self.Ticket.OriginatingHostName or "" 552 os.environ["PYKOTAJOBID"] = self.Ticket.JobId 665 553 os.environ["PYKOTAUSERNAME"] = self.UserName 666 os.environ["PYKOTAORIGINALUSERNAME"] = self. OriginalUserName667 os.environ["PYKOTATITLE"] = self.Ti tle668 os.environ["PYKOTACOPIES"] = str(self. Copies)669 os.environ["PYKOTAOPTIONS"] = self. Options670 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 "" 672 560 os.environ["PYKOTAORIGINALJOBBILLING"] = self.OriginalJobBillingCode or "" 673 os.environ["PYKOTACONTROLFILE"] = self.ControlFile674 561 os.environ["PYKOTAPRINTERHOSTNAME"] = self.PrinterHostName 675 562 os.environ["PYKOTAPRECOMPUTEDJOBSIZE"] = str(self.softwareJobSize) … … 763 650 return "%s@%s(%s) => %s" % (self.UserName, \ 764 651 self.PrinterName, \ 765 self. JobId, \652 self.Ticket.JobId, \ 766 653 message) 767 654 except : … … 970 857 self.logdebug("Retrieving billing code information from the database...") 971 858 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) 974 861 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) 976 863 else : 977 msg = "Unknown billing code [%s] : " % self. JobBillingCode864 msg = "Unknown billing code [%s] : " % self.Ticket.BillingCode 978 865 (newaction, script) = self.config.getUnknownBillingCode(self.PrinterName) 979 866 if newaction == "CREATE" : 980 867 self.logdebug(msg + "will be created.") 981 868 self.storage.addBillingCode(self.BillingCode) 982 self.BillingCode = self.storage.getBillingCode(self. JobBillingCode)869 self.BillingCode = self.storage.getBillingCode(self.Ticket.BillingCode) 983 870 if self.BillingCode.Exists : 984 871 self.logdebug(msg + "has been created.") … … 1311 1198 1312 1199 # 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.Ti tle, 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, \ 1317 1204 self.softwareJobSize, self.softwareJobPrice) 1318 1205 self.printInfo(_("Job added to history.")) … … 1363 1250 loopcnt = 1 1364 1251 while True : 1365 if self. InputFile is None :1252 if self.Ticket.FileName is None : 1366 1253 infile = open(self.DataFile, "rb") 1367 1254 else : 1368 1255 infile = None 1369 1256 retcode = self.runOriginalBackend(infile) 1370 if self. InputFile is None :1257 if self.Ticket.FileName is None : 1371 1258 infile.close() 1372 1259 if not retcode : … … 1474 1361 retcode = wrapper.mainWork() 1475 1362 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") 1477 1364 retcode = 0 1478 1365 except SystemExit, err : -
pykota/trunk/conf/pykota.conf.sample
r3242 r3252 986 986 # PYKOTAFILENAME : The name of the file which contains the job's datas or 987 987 # empty if datas come from stdin 988 # PYKOTACONTROLFILE : The name of the IPP message file989 988 # PYKOTAMD5SUM : Contains an hexadecimal digest of the md5 sum of the job's datas 990 989 # PYKOTAPHASE : BEFORE or AFTER the job is sent to the printer -
pykota/trunk/pykota/accounters/ink.py
r3245 r3252 88 88 jobsize = len(pages) 89 89 try : 90 if self.filter. InputFile is not None :90 if self.filter.Ticket.FileName is not None : 91 91 # when a filename is passed as an argument, the backend 92 92 # must generate the correct number of copies. 93 jobsize *= self.filter. Copies94 self.inkUsage *= self.filter. Copies93 jobsize *= self.filter.Ticket.Copies 94 self.inkUsage *= self.filter.Ticket.Copies 95 95 except AttributeError : # When not run from the cupspykota backend 96 96 pass -
pykota/trunk/pykota/accounters/software.py
r3245 r3252 67 67 else : 68 68 try : 69 if self.filter. InputFile is not None :69 if self.filter.Ticket.FileName is not None : 70 70 # when a filename is passed as an argument, the backend 71 71 # must generate the correct number of copies. 72 jobsize *= self.filter. Copies72 jobsize *= self.filter.Ticket.Copies 73 73 except AttributeError : # When not run from the cupspykota backend 74 74 pass … … 128 128 129 129 pagecounter = pagecounter or 0 130 if self.filter. InputFile is not None :130 if self.filter.Ticket.FileName is not None : 131 131 # when a filename is passed as an argument, the backend 132 132 # must generate the correct number of copies. 133 pagecounter *= self.filter. Copies133 pagecounter *= self.filter.Ticket.Copies 134 134 135 135 return pagecounter -
pykota/trunk/pykota/cups.py
r3249 r3252 30 30 raise RuntimeError, "The python-pkipplib module is now mandatory. You can download pkipplib from http://www.pykota.com/" 31 31 32 class Job :32 class JobTicket : 33 33 """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) : 35 36 """Initializes a print job's information.""" 36 37 self.JobId = jobid 38 self.PrinterName = printername 37 39 self.Copies = copies 38 40 self.FileName = filename 41 self.Options = options 39 42 self.Charset = None 40 self.UserName = None 43 self.OriginatingUserName = None 44 self.OriginalUserName = None 41 45 self.Title = None 42 46 self.BillingCode = None … … 61 65 def retrieveAttributesFromCUPS(self) : 62 66 """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() 63 71 server = pkipplib.CUPS() # TODO : username and password and/or encryption 64 72 answer = server.getJobAttributes(self.JobId) … … 66 74 raise PyKotaToolError, "Network error while querying the CUPS server : %s" \ 67 75 % 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") 70 78 (dummy, self.Title) = self.getAttributeTypeAndValue(answer, "job-name") 71 79 (dummy, self.BillingCode) = self.getAttributeTypeAndValue(answer, "job-billing") … … 75 83 (dummy, self.TimeAtProcessing) = self.getAttributeTypeAndValue(answer, "time-at-processing") 76 84 (dummy, self.MimeType) = self.getAttributeTypeAndValue(answer, "document-format") 85 self.OriginalUserName = self.OriginatingUserName[:] 77 86 78 87 if __name__ == "__main__" : … … 81 90 sys.stderr.write("usage : python cups.py jobid\n") 82 91 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", 85 94 "Title", "BillingCode", "OriginatingHostName", 86 95 "TimeAtCreation", "TimeAtProcessing", "UUID",