- Timestamp:
- 04/15/10 01:27:45 (14 years ago)
- Files:
-
- 1 modified
Legend:
- Unmodified
- Added
- Removed
-
pykota/branches/1.26_fixes/pykota/storages/ldapstorage.py
r3184 r3522 14 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 15 # GNU General Public License for more details. 16 # 16 # 17 17 # You should have received a copy of the GNU General Public License 18 18 # along with this program; if not, write to the Free Software … … 25 25 """This module defines a class to access to an LDAP database backend. 26 26 27 My IANA assigned number, for 28 "Conseil Internet & Logiciels Libres, J�me Alet" 27 My IANA assigned number, for 28 "Conseil Internet & Logiciels Libres, J�me Alet" 29 29 is 16868. Use this as a base to extend the LDAP schema. 30 30 """ … … 47 47 import ldap 48 48 import ldap.modlist 49 except ImportError : 49 except ImportError : 50 50 raise PyKotaStorageError, "This python version (%s) doesn't seem to have the python-ldap module installed correctly." % sys.version.split()[0] 51 else : 51 else : 52 52 try : 53 53 from ldap.cidict import cidict 54 except ImportError : 54 except ImportError : 55 55 import UserDict 56 56 sys.stderr.write("ERROR: PyKota requires a newer version of python-ldap. Workaround activated. Please upgrade python-ldap !\n") 57 57 class cidict(UserDict.UserDict) : 58 58 pass # Fake it all, and don't care for case insensitivity : users who need it will have to upgrade. 59 59 60 60 class Storage(BaseStorage) : 61 61 def __init__(self, pykotatool, host, dbname, user, passwd) : … … 67 67 self.savedpasswd = passwd 68 68 self.secondStageInit() 69 70 def secondStageInit(self) : 69 70 def secondStageInit(self) : 71 71 """Second stage initialisation.""" 72 72 BaseStorage.__init__(self, self.savedtool) … … 76 76 try : 77 77 self.tool.logdebug("Trying to open database (host=%s, dbname=%s, user=%s)..." % (self.savedhost, self.saveddbname, self.saveduser)) 78 self.database = ldap.initialize(self.savedhost) 78 self.database = ldap.initialize(self.savedhost) 79 79 if self.info["ldaptls"] : 80 80 # we want TLS … … 84 84 self.database.simple_bind_s(self.saveduser, self.savedpasswd) 85 85 self.basedn = self.saveddbname 86 except ldap.SERVER_DOWN : 86 except ldap.SERVER_DOWN : 87 87 message = "LDAP backend for PyKota seems to be down !" 88 88 self.tool.printInfo("%s" % message, "error") 89 89 self.tool.printInfo("Trying again in 2 seconds...", "warn") 90 90 time.sleep(2) 91 except ldap.LDAPError : 91 except ldap.LDAPError : 92 92 message = "Unable to connect to LDAP server %s as %s." % (self.savedhost, self.saveduser) 93 93 self.tool.printInfo("%s" % message, "error") 94 94 self.tool.printInfo("Trying again in 2 seconds...", "warn") 95 95 time.sleep(2) 96 else : 96 else : 97 97 self.useldapcache = self.tool.config.getLDAPCache() 98 98 if self.useldapcache : … … 102 102 self.tool.logdebug("Database opened (host=%s, dbname=%s, user=%s)" % (self.savedhost, self.saveddbname, self.saveduser)) 103 103 return # All is fine here. 104 raise PyKotaStorageError, message 105 106 def close(self) : 104 raise PyKotaStorageError, message 105 106 def close(self) : 107 107 """Closes the database connection.""" 108 108 if not self.closed : … … 110 110 self.closed = 1 111 111 self.tool.logdebug("Database closed.") 112 113 def genUUID(self) : 112 113 def genUUID(self) : 114 114 """Generates an unique identifier. 115 115 116 116 TODO : this one is not unique accross several print servers, but should be sufficient for testing. 117 117 """ 118 118 return md5.md5("%s-%s" % (time.time(), random.random())).hexdigest() 119 120 def normalizeFields(self, fields) : 119 120 def normalizeFields(self, fields) : 121 121 """Ensure all items are lists.""" 122 122 for (k, v) in fields.items() : … … 124 124 if not v : 125 125 del fields[k] 126 else : 126 else : 127 127 fields[k] = [ v ] 128 return fields 129 130 def beginTransaction(self) : 128 return fields 129 130 def beginTransaction(self) : 131 131 """Starts a transaction.""" 132 132 self.tool.logdebug("Transaction begins... WARNING : No transactions in LDAP !") 133 134 def commitTransaction(self) : 133 134 def commitTransaction(self) : 135 135 """Commits a transaction.""" 136 136 self.tool.logdebug("Transaction committed. WARNING : No transactions in LDAP !") 137 138 def rollbackTransaction(self) : 137 138 def rollbackTransaction(self) : 139 139 """Rollbacks a transaction.""" 140 140 self.tool.logdebug("Transaction aborted. WARNING : No transaction in LDAP !") 141 141 142 142 def doSearch(self, key, fields=None, base="", scope=ldap.SCOPE_SUBTREE, flushcache=0) : 143 143 """Does an LDAP search query.""" … … 150 150 # retrieve ALL user defined attributes ("*") 151 151 # + the createTimestamp attribute, needed by job history 152 # 152 # 153 153 # This may not work with all LDAP servers 154 # but works at least in OpenLDAP (2.1.25) 154 # but works at least in OpenLDAP (2.1.25) 155 155 # and iPlanet Directory Server (5.1 SP3) 156 fields = ["*", "createTimestamp"] 157 156 fields = ["*", "createTimestamp"] 157 158 158 if self.useldapcache and (not flushcache) and (scope == ldap.SCOPE_BASE) and self.ldapcache.has_key(base) : 159 159 entry = self.ldapcache[base] … … 163 163 self.tool.logdebug("QUERY : Filter : %s, BaseDN : %s, Scope : %s, Attributes : %s" % (key, base, scope, fields)) 164 164 result = self.database.search_s(base, scope, key, fields) 165 except ldap.NO_SUCH_OBJECT, msg : 165 except ldap.NO_SUCH_OBJECT, msg : 166 166 raise PyKotaStorageError, (_("Search base %s doesn't seem to exist. Probable misconfiguration. Please double check /etc/pykota/pykota.conf : %s") % (base, msg)) 167 except ldap.LDAPError, msg : 167 except ldap.LDAPError, msg : 168 168 message = (_("Search for %s(%s) from %s(scope=%s) returned no answer.") % (key, fields, base, scope)) + " : %s" % str(msg) 169 169 self.tool.printInfo("LDAP error : %s" % message, "error") … … 171 171 self.close() 172 172 self.secondStageInit() 173 else : 173 else : 174 174 self.tool.logdebug("QUERY : Result : %s" % result) 175 175 result = [ (dn, cidict(attrs)) for (dn, attrs) in result ] … … 180 180 return result 181 181 raise PyKotaStorageError, message 182 182 183 183 def doAdd(self, dn, fields) : 184 184 """Adds an entry in the LDAP directory.""" … … 191 191 self.tool.logdebug("%s" % entry) 192 192 self.database.add_s(dn, entry) 193 except ldap.ALREADY_EXISTS, msg : 193 except ldap.ALREADY_EXISTS, msg : 194 194 raise PyKotaStorageError, "Entry %s already exists : %s" % (dn, str(msg)) 195 195 except ldap.LDAPError, msg : … … 205 205 return dn 206 206 raise PyKotaStorageError, message 207 207 208 208 def doDelete(self, dn) : 209 209 """Deletes an entry from the LDAP directory.""" … … 213 213 self.tool.logdebug("QUERY : Delete(%s)" % dn) 214 214 self.database.delete_s(dn) 215 except ldap.NO_SUCH_OBJECT : 215 except ldap.NO_SUCH_OBJECT : 216 216 self.tool.printInfo("Entry %s was already missing before we deleted it. This **MAY** be normal." % dn, "info") 217 217 except ldap.LDAPError, msg : … … 221 221 self.close() 222 222 self.secondStageInit() 223 else : 223 else : 224 224 if self.useldapcache : 225 225 try : 226 226 self.tool.logdebug("LDAP cache del %s" % dn) 227 227 del self.ldapcache[dn] 228 except KeyError : 228 except KeyError : 229 229 pass 230 return 230 return 231 231 raise PyKotaStorageError, message 232 232 233 233 def doModify(self, dn, fields, ignoreold=1, flushcache=0) : 234 234 """Modifies an entry in the LDAP directory.""" … … 245 245 if k != "createTimestamp" : 246 246 oldentry[k] = v 247 else : 247 else : 248 248 self.tool.logdebug("LDAP cache miss %s" % dn) 249 249 oldentry = self.doSearch("objectClass=*", base=dn, scope=ldap.SCOPE_BASE)[0][1] 250 else : 250 else : 251 251 oldentry = self.doSearch("objectClass=*", base=dn, scope=ldap.SCOPE_BASE, flushcache=flushcache)[0][1] 252 252 for (k, v) in fields.items() : … … 254 254 try : 255 255 oldvalue = v["convert"](oldentry.get(k, [0])[0]) 256 except ValueError : 256 except ValueError : 257 257 self.tool.logdebug("Error converting %s with %s(%s)" % (oldentry.get(k), k, v)) 258 258 oldvalue = 0 259 259 if v["operator"] == '+' : 260 260 newvalue = oldvalue + v["value"] 261 else : 261 else : 262 262 newvalue = oldvalue - v["value"] 263 263 fields[k] = str(newvalue) … … 287 287 try : 288 288 del cachedentry[mtyp] 289 except KeyError : 289 except KeyError : 290 290 pass 291 291 self.tool.logdebug("LDAP cache update %s => %s" % (dn, cachedentry)) 292 292 return dn 293 293 raise PyKotaStorageError, message 294 294 295 295 def filterNames(self, records, attribute, patterns=None) : 296 296 """Returns a list of 'attribute' from a list of records. 297 297 298 298 Logs any missing attribute. 299 """ 299 """ 300 300 result = [] 301 301 for (dn, record) in records : … … 308 308 if (not isinstance(patterns, type([]))) and (not isinstance(patterns, type(()))) : 309 309 patterns = [ patterns ] 310 if self.tool.matchString(attrval, patterns) : 310 if self.tool.matchString(attrval, patterns) : 311 311 result.append(attrval) 312 else : 312 else : 313 313 result.append(attrval) 314 return result 315 316 def getAllBillingCodes(self, billingcode=None) : 314 return result 315 316 def getAllBillingCodes(self, billingcode=None) : 317 317 """Extracts all billing codes or only the billing codes matching the optional parameter.""" 318 318 ldapfilter = "objectClass=pykotaBilling" … … 320 320 if result : 321 321 return [self.databaseToUserCharset(bc) for bc in self.filterNames(result, "pykotaBillingCode", billingcode)] 322 else : 322 else : 323 323 return [] 324 325 def getAllPrintersNames(self, printername=None) : 324 325 def getAllPrintersNames(self, printername=None) : 326 326 """Extracts all printer names or only the printers' names matching the optional parameter.""" 327 327 ldapfilter = "objectClass=pykotaPrinter" … … 329 329 if result : 330 330 return self.filterNames(result, "pykotaPrinterName", printername) 331 else : 331 else : 332 332 return [] 333 334 def getAllUsersNames(self, username=None) : 333 334 def getAllUsersNames(self, username=None) : 335 335 """Extracts all user names or only the users' names matching the optional parameter.""" 336 336 ldapfilter = "objectClass=pykotaAccount" … … 338 338 if result : 339 339 return self.filterNames(result, "pykotaUserName", username) 340 else : 340 else : 341 341 return [] 342 343 def getAllGroupsNames(self, groupname=None) : 342 343 def getAllGroupsNames(self, groupname=None) : 344 344 """Extracts all group names or only the groups' names matching the optional parameter.""" 345 345 ldapfilter = "objectClass=pykotaGroup" … … 347 347 if result : 348 348 return self.filterNames(result, "pykotaGroupName", groupname) 349 else : 349 else : 350 350 return [] 351 351 352 352 def getUserNbJobsFromHistory(self, user) : 353 353 """Returns the number of jobs the user has in history.""" 354 354 result = self.doSearch("(&(pykotaUserName=%s)(objectClass=pykotaJob))" % self.userCharsetToDatabase(user.Name), None, base=self.info["jobbase"]) 355 355 return len(result) 356 357 def getUserFromBackend(self, username) : 356 357 def getUserFromBackend(self, username) : 358 358 """Extracts user information given its name.""" 359 359 user = StorageUser(self, username) … … 376 376 if user.AccountBalance[0].upper() == "NONE" : 377 377 user.AccountBalance = None 378 else : 378 else : 379 379 user.AccountBalance = float(user.AccountBalance[0]) 380 user.AccountBalance = user.AccountBalance or 0.0 380 user.AccountBalance = user.AccountBalance or 0.0 381 381 user.LifeTimePaid = fields.get("pykotaLifeTimePaid") 382 382 user.OverCharge = float(fields.get("pykotaOverCharge", [1.0])[0]) … … 384 384 if user.LifeTimePaid[0].upper() == "NONE" : 385 385 user.LifeTimePaid = None 386 else : 386 else : 387 387 user.LifeTimePaid = float(user.LifeTimePaid[0]) 388 user.LifeTimePaid = user.LifeTimePaid or 0.0 388 user.LifeTimePaid = user.LifeTimePaid or 0.0 389 389 user.Payments = [] 390 390 for payment in fields.get("pykotaPayments", []) : … … 395 395 (date, amount) = payment.split(" # ") 396 396 description = "" 397 else : 397 else : 398 398 description = self.databaseToUserCharset(base64.decodestring(description)) 399 399 user.Payments.append((date, float(amount), description)) 400 400 user.Exists = True 401 401 return user 402 403 def getGroupFromBackend(self, groupname) : 402 403 def getGroupFromBackend(self, groupname) : 404 404 """Extracts group information given its name.""" 405 405 group = StorageGroup(self, groupname) … … 409 409 fields = result[0][1] 410 410 group.ident = result[0][0] 411 group.Name = fields.get("pykotaGroupName", [self.databaseToUserCharset(groupname)])[0] 411 group.Name = fields.get("pykotaGroupName", [self.databaseToUserCharset(groupname)])[0] 412 412 group.Description = self.databaseToUserCharset(fields.get("description", [None])[0]) 413 413 group.LimitBy = fields.get("pykotaLimitBy", ["quota"])[0] … … 420 420 group.Exists = True 421 421 return group 422 423 def getPrinterFromBackend(self, printername) : 422 423 def getPrinterFromBackend(self, printername) : 424 424 """Extracts printer information given its name : returns first matching printer.""" 425 425 printer = StoragePrinter(self, printername) … … 434 434 fields = result[0][1] # take only first matching printer, ignore the rest 435 435 printer.ident = result[0][0] 436 printer.Name = fields.get("pykotaPrinterName", [self.databaseToUserCharset(printername)])[0] 436 printer.Name = fields.get("pykotaPrinterName", [self.databaseToUserCharset(printername)])[0] 437 437 printer.PricePerJob = float(fields.get("pykotaPricePerJob", [0.0])[0]) 438 438 printer.PricePerPage = float(fields.get("pykotaPricePerPage", [0.0])[0]) … … 444 444 printer.PassThrough = 0 445 445 printer.uniqueMember = fields.get("uniqueMember", []) 446 printer.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 446 printer.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 447 447 printer.Exists = True 448 return printer 449 450 def getUserPQuotaFromBackend(self, user, printer) : 448 return printer 449 450 def getUserPQuotaFromBackend(self, user, printer) : 451 451 """Extracts a user print quota.""" 452 452 userpquota = StorageUserPQuota(self, user, printer) … … 454 454 if self.info["userquotabase"].lower() == "user" : 455 455 base = user.ident 456 else : 456 else : 457 457 base = self.info["userquotabase"] 458 458 result = self.doSearch("(&(objectClass=pykotaUserPQuota)(pykotaUserName=%s)(pykotaPrinterName=%s))" % \ … … 470 470 if userpquota.SoftLimit[0].upper() == "NONE" : 471 471 userpquota.SoftLimit = None 472 else : 472 else : 473 473 userpquota.SoftLimit = int(userpquota.SoftLimit[0]) 474 474 userpquota.HardLimit = fields.get("pykotaHardLimit") … … 476 476 if userpquota.HardLimit[0].upper() == "NONE" : 477 477 userpquota.HardLimit = None 478 elif userpquota.HardLimit is not None : 478 elif userpquota.HardLimit is not None : 479 479 userpquota.HardLimit = int(userpquota.HardLimit[0]) 480 480 userpquota.DateLimit = fields.get("pykotaDateLimit") 481 481 if userpquota.DateLimit is not None : 482 if userpquota.DateLimit[0].upper() == "NONE" : 482 if userpquota.DateLimit[0].upper() == "NONE" : 483 483 userpquota.DateLimit = None 484 else : 484 else : 485 485 userpquota.DateLimit = userpquota.DateLimit[0] 486 486 userpquota.MaxJobSize = fields.get("pykotaMaxJobSize") … … 488 488 if userpquota.MaxJobSize[0].upper() == "NONE" : 489 489 userpquota.MaxJobSize = None 490 else : 490 else : 491 491 userpquota.MaxJobSize = int(userpquota.MaxJobSize[0]) 492 492 userpquota.Exists = True 493 493 return userpquota 494 495 def getGroupPQuotaFromBackend(self, group, printer) : 494 495 def getGroupPQuotaFromBackend(self, group, printer) : 496 496 """Extracts a group print quota.""" 497 497 grouppquota = StorageGroupPQuota(self, group, printer) … … 499 499 if self.info["groupquotabase"].lower() == "group" : 500 500 base = group.ident 501 else : 501 else : 502 502 base = self.info["groupquotabase"] 503 503 result = self.doSearch("(&(objectClass=pykotaGroupPQuota)(pykotaGroupName=%s)(pykotaPrinterName=%s))" % \ … … 512 512 if grouppquota.SoftLimit[0].upper() == "NONE" : 513 513 grouppquota.SoftLimit = None 514 else : 514 else : 515 515 grouppquota.SoftLimit = int(grouppquota.SoftLimit[0]) 516 516 grouppquota.HardLimit = fields.get("pykotaHardLimit") … … 518 518 if grouppquota.HardLimit[0].upper() == "NONE" : 519 519 grouppquota.HardLimit = None 520 else : 520 else : 521 521 grouppquota.HardLimit = int(grouppquota.HardLimit[0]) 522 522 grouppquota.DateLimit = fields.get("pykotaDateLimit") 523 523 if grouppquota.DateLimit is not None : 524 if grouppquota.DateLimit[0].upper() == "NONE" : 524 if grouppquota.DateLimit[0].upper() == "NONE" : 525 525 grouppquota.DateLimit = None 526 else : 526 else : 527 527 grouppquota.DateLimit = grouppquota.DateLimit[0] 528 528 grouppquota.MaxJobSize = fields.get("pykotaMaxJobSize") … … 530 530 if grouppquota.MaxJobSize[0].upper() == "NONE" : 531 531 grouppquota.MaxJobSize = None 532 else : 532 else : 533 533 grouppquota.MaxJobSize = int(grouppquota.MaxJobSize[0]) 534 534 grouppquota.PageCounter = 0 … … 545 545 ["pykotaPageCounter", "pykotaLifePageCounter"], base=base) 546 546 if result : 547 for userpquota in result : 547 for userpquota in result : 548 548 grouppquota.PageCounter += int(userpquota[1].get("pykotaPageCounter", [0])[0] or 0) 549 549 grouppquota.LifePageCounter += int(userpquota[1].get("pykotaLifePageCounter", [0])[0] or 0) 550 550 grouppquota.Exists = True 551 551 return grouppquota 552 553 def getPrinterLastJobFromBackend(self, printer) : 552 553 def getPrinterLastJobFromBackend(self, printer) : 554 554 """Extracts a printer's last job information.""" 555 555 lastjob = StorageLastJob(self, printer) … … 564 564 result = None 565 565 try : 566 result = self.doSearch("objectClass=pykotaJob", [ "pykotaJobSizeBytes", 567 "pykotaHostName", 568 "pykotaUserName", 569 "pykotaPrinterName", 570 "pykotaJobId", 571 "pykotaPrinterPageCounter", 572 "pykotaJobSize", 573 "pykotaAction", 574 "pykotaJobPrice", 575 "pykotaFileName", 576 "pykotaTitle", 577 "pykotaCopies", 578 "pykotaOptions", 579 "pykotaBillingCode", 580 "pykotaPages", 581 "pykotaMD5Sum", 566 result = self.doSearch("objectClass=pykotaJob", [ "pykotaJobSizeBytes", 567 "pykotaHostName", 568 "pykotaUserName", 569 "pykotaPrinterName", 570 "pykotaJobId", 571 "pykotaPrinterPageCounter", 572 "pykotaJobSize", 573 "pykotaAction", 574 "pykotaJobPrice", 575 "pykotaFileName", 576 "pykotaTitle", 577 "pykotaCopies", 578 "pykotaOptions", 579 "pykotaBillingCode", 580 "pykotaPages", 581 "pykotaMD5Sum", 582 582 "pykotaPrecomputedJobSize", 583 583 "pykotaPrecomputedJobPrice", 584 "createTimestamp" ], 584 "createTimestamp" ], 585 585 base="cn=%s,%s" % (lastjobident, self.info["jobbase"]), scope=ldap.SCOPE_BASE) 586 except PyKotaStorageError : 587 pass # Last job entry exists, but job probably doesn't exist anymore. 586 except PyKotaStorageError : 587 pass # Last job entry exists, but job probably doesn't exist anymore. 588 588 if result : 589 589 fields = result[0][1] … … 594 594 try : 595 595 lastjob.JobSize = int(fields.get("pykotaJobSize", [0])[0]) 596 except ValueError : 596 except ValueError : 597 597 lastjob.JobSize = None 598 try : 598 try : 599 599 lastjob.JobPrice = float(fields.get("pykotaJobPrice", [0.0])[0]) 600 except ValueError : 600 except ValueError : 601 601 lastjob.JobPrice = None 602 602 lastjob.JobAction = fields.get("pykotaAction", [""])[0] 603 lastjob.JobFileName = self.databaseToUserCharset(fields.get("pykotaFileName", [""])[0]) 604 lastjob.JobTitle = self.databaseToUserCharset(fields.get("pykotaTitle", [""])[0]) 603 lastjob.JobFileName = self.databaseToUserCharset(fields.get("pykotaFileName", [""])[0]) 604 lastjob.JobTitle = self.databaseToUserCharset(fields.get("pykotaTitle", [""])[0]) 605 605 lastjob.JobCopies = int(fields.get("pykotaCopies", [0])[0]) 606 lastjob.JobOptions = self.databaseToUserCharset(fields.get("pykotaOptions", [""])[0]) 606 lastjob.JobOptions = self.databaseToUserCharset(fields.get("pykotaOptions", [""])[0]) 607 607 lastjob.JobHostName = fields.get("pykotaHostName", [""])[0] 608 608 lastjob.JobSizeBytes = fields.get("pykotaJobSizeBytes", [0L])[0] … … 612 612 try : 613 613 lastjob.PrecomputedJobSize = int(fields.get("pykotaPrecomputedJobSize", [0])[0]) 614 except ValueError : 614 except ValueError : 615 615 lastjob.PrecomputedJobSize = None 616 try : 616 try : 617 617 lastjob.PrecomputedJobPrice = float(fields.get("pykotaPrecomputedJobPrice", [0.0])[0]) 618 except ValueError : 618 except ValueError : 619 619 lastjob.PrecomputedJobPrice = None 620 620 if lastjob.JobTitle == lastjob.JobFileName == lastjob.JobOptions == "hidden" : … … 625 625 lastjob.Exists = True 626 626 return lastjob 627 628 def getGroupMembersFromBackend(self, group) : 627 628 def getGroupMembersFromBackend(self, group) : 629 629 """Returns the group's members list.""" 630 630 groupmembers = [] … … 637 637 for username in result[0][1].get(self.info["groupmembers"], []) : 638 638 groupmembers.append(self.getUser(self.databaseToUserCharset(username))) 639 return groupmembers 640 641 def getUserGroupsFromBackend(self, user) : 639 return groupmembers 640 641 def getUserGroupsFromBackend(self, user) : 642 642 """Returns the user's groups list.""" 643 643 groups = [] … … 657 657 if group.LimitBy is not None : 658 658 group.LimitBy = group.LimitBy[0] 659 else : 659 else : 660 660 group.LimitBy = "quota" 661 661 group.AccountBalance = 0.0 … … 668 668 self.cacheEntry("GROUPS", group.Name, group) 669 669 groups.append(group) 670 return groups 671 672 def getParentPrintersFromBackend(self, printer) : 670 return groups 671 672 def getParentPrintersFromBackend(self, printer) : 673 673 """Get all the printer groups this printer is a member of.""" 674 674 pgroups = [] … … 684 684 pgroups.append(parentprinter) 685 685 return pgroups 686 686 687 687 def getMatchingPrinters(self, printerpattern) : 688 688 """Returns the list of all printers for which name matches a certain pattern.""" … … 694 694 if result : 695 695 patterns = printerpattern.split(",") 696 try : 697 patdict = {}.fromkeys(patterns) 698 except AttributeError : 699 # Python v2.2 or earlier 700 patdict = {} 701 for p in patterns : 702 patdict[p] = None 696 patdict = {}.fromkeys(patterns) 703 697 for (printerid, fields) in result : 704 698 printername = self.databaseToUserCharset(fields.get("pykotaPrinterName", [""])[0] or fields.get(self.info["printerrdn"], [""])[0]) … … 715 709 printer.PassThrough = 0 716 710 printer.uniqueMember = fields.get("uniqueMember", []) 717 printer.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 711 printer.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 718 712 printer.Exists = True 719 713 printers.append(printer) 720 714 self.cacheEntry("PRINTERS", printer.Name, printer) 721 return printers 722 715 return printers 716 723 717 def getMatchingUsers(self, userpattern) : 724 718 """Returns the list of all users for which name matches a certain pattern.""" … … 730 724 if result : 731 725 patterns = userpattern.split(",") 732 try : 733 patdict = {}.fromkeys(patterns) 734 except AttributeError : 735 # Python v2.2 or earlier 736 patdict = {} 737 for p in patterns : 738 patdict[p] = None 726 patdict = {}.fromkeys(patterns) 739 727 for (userid, fields) in result : 740 728 username = self.databaseToUserCharset(fields.get("pykotaUserName", [""])[0] or fields.get(self.info["userrdn"], [""])[0]) … … 744 732 user.Email = fields.get(self.info["usermail"], [None])[0] 745 733 user.LimitBy = fields.get("pykotaLimitBy", ["quota"])[0] 746 user.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 734 user.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 747 735 uname = self.userCharsetToDatabase(username) 748 736 result = self.doSearch("(&(objectClass=pykotaAccountBalance)(|(pykotaUserName=%s)(%s=%s)))" % \ … … 760 748 if user.AccountBalance[0].upper() == "NONE" : 761 749 user.AccountBalance = None 762 else : 750 else : 763 751 user.AccountBalance = float(user.AccountBalance[0]) 764 user.AccountBalance = user.AccountBalance or 0.0 752 user.AccountBalance = user.AccountBalance or 0.0 765 753 user.LifeTimePaid = fields.get("pykotaLifeTimePaid") 766 754 if user.LifeTimePaid is not None : 767 755 if user.LifeTimePaid[0].upper() == "NONE" : 768 756 user.LifeTimePaid = None 769 else : 757 else : 770 758 user.LifeTimePaid = float(user.LifeTimePaid[0]) 771 user.LifeTimePaid = user.LifeTimePaid or 0.0 759 user.LifeTimePaid = user.LifeTimePaid or 0.0 772 760 user.Payments = [] 773 761 for payment in fields.get("pykotaPayments", []) : … … 778 766 (date, amount) = payment.split(" # ") 779 767 description = "" 780 else : 768 else : 781 769 description = self.databaseToUserCharset(base64.decodestring(description)) 782 770 user.Payments.append((date, float(amount), description)) … … 784 772 users.append(user) 785 773 self.cacheEntry("USERS", user.Name, user) 786 return users 787 774 return users 775 788 776 def getMatchingGroups(self, grouppattern) : 789 777 """Returns the list of all groups for which name matches a certain pattern.""" … … 795 783 if result : 796 784 patterns = grouppattern.split(",") 797 try : 798 patdict = {}.fromkeys(patterns) 799 except AttributeError : 800 # Python v2.2 or earlier 801 patdict = {} 802 for p in patterns : 803 patdict[p] = None 785 patdict = {}.fromkeys(patterns) 804 786 for (groupid, fields) in result : 805 787 groupname = self.databaseToUserCharset(fields.get("pykotaGroupName", [""])[0] or fields.get(self.info["grouprdn"], [""])[0]) … … 807 789 group = StorageGroup(self, groupname) 808 790 group.ident = groupid 809 group.Name = fields.get("pykotaGroupName", [self.databaseToUserCharset(groupname)])[0] 791 group.Name = fields.get("pykotaGroupName", [self.databaseToUserCharset(groupname)])[0] 810 792 group.LimitBy = fields.get("pykotaLimitBy", ["quota"])[0] 811 group.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 793 group.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 812 794 group.AccountBalance = 0.0 813 795 group.LifeTimePaid = 0.0 … … 820 802 self.cacheEntry("GROUPS", group.Name, group) 821 803 return groups 822 823 def getPrinterUsersAndQuotas(self, printer, names=["*"]) : 804 805 def getPrinterUsersAndQuotas(self, printer, names=["*"]) : 824 806 """Returns the list of users who uses a given printer, along with their quotas.""" 825 807 usersandquotas = [] … … 846 828 if userpquota.SoftLimit[0].upper() == "NONE" : 847 829 userpquota.SoftLimit = None 848 else : 830 else : 849 831 userpquota.SoftLimit = int(userpquota.SoftLimit[0]) 850 832 userpquota.HardLimit = fields.get("pykotaHardLimit") … … 852 834 if userpquota.HardLimit[0].upper() == "NONE" : 853 835 userpquota.HardLimit = None 854 elif userpquota.HardLimit is not None : 836 elif userpquota.HardLimit is not None : 855 837 userpquota.HardLimit = int(userpquota.HardLimit[0]) 856 838 userpquota.DateLimit = fields.get("pykotaDateLimit") 857 839 if userpquota.DateLimit is not None : 858 if userpquota.DateLimit[0].upper() == "NONE" : 840 if userpquota.DateLimit[0].upper() == "NONE" : 859 841 userpquota.DateLimit = None 860 else : 842 else : 861 843 userpquota.DateLimit = userpquota.DateLimit[0] 862 844 userpquota.Exists = True 863 845 usersandquotas.append((user, userpquota)) 864 846 self.cacheEntry("USERPQUOTAS", "%s@%s" % (user.Name, printer.Name), userpquota) 865 usersandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name)) 847 usersandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name)) 866 848 return usersandquotas 867 868 def getPrinterGroupsAndQuotas(self, printer, names=["*"]) : 849 850 def getPrinterGroupsAndQuotas(self, printer, names=["*"]) : 869 851 """Returns the list of groups which uses a given printer, along with their quotas.""" 870 852 groupsandquotas = [] … … 884 866 grouppquota = self.getGroupPQuota(group, printer) 885 867 groupsandquotas.append((group, grouppquota)) 886 groupsandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name)) 868 groupsandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name)) 887 869 return groupsandquotas 888 870 889 871 def addPrinter(self, printer) : 890 872 """Adds a printer to the quota storage, returns the old value if it already exists.""" … … 902 884 "pykotaPricePerPage" : str(printer.PricePerPage or 0.0), 903 885 "pykotaPricePerJob" : str(printer.PricePerJob or 0.0), 904 } 886 } 905 887 dn = "%s=%s,%s" % (self.info["printerrdn"], printername, self.info["printerbase"]) 906 888 self.doAdd(dn, fields) 907 889 printer.isDirty = False 908 890 return None # the entry created doesn't need further modification 909 910 def addUser(self, user) : 891 892 def addUser(self, user) : 911 893 """Adds a user to the quota storage, returns the old value if it already exists.""" 912 894 oldentry = self.getUser(user.Name) … … 919 901 "description" : self.userCharsetToDatabase(user.Description or ""), 920 902 self.info["usermail"] : user.Email or "", 921 } 922 903 } 904 923 905 mustadd = 1 924 906 if self.info["newuser"].lower() != 'below' : … … 938 920 fields.update({ "pykotaBalance" : str(user.AccountBalance or 0.0), 939 921 "pykotaOverCharge" : str(user.OverCharge), 940 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), }) 922 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), }) 941 923 self.doModify(dn, fields) 942 924 mustadd = 0 943 925 else : 944 926 message = _("Unable to find an existing objectClass %s entry with %s=%s to attach pykotaAccount objectClass") % (where, self.info["userrdn"], user.Name) 945 if action.lower() == "warn" : 927 if action.lower() == "warn" : 946 928 self.tool.printInfo(_("%s. A new entry will be created instead.") % message, "warn") 947 929 else : # 'fail' or incorrect setting 948 930 raise PyKotaStorageError, "%s. Action aborted. Please check your configuration." % message 949 931 950 932 if mustadd : 951 if self.info["userbase"] == self.info["balancebase"] : 933 if self.info["userbase"] == self.info["balancebase"] : 952 934 fields = { self.info["userrdn"] : uname, 953 935 "objectClass" : ["pykotaObject", "pykotaAccount", "pykotaAccountBalance"], … … 955 937 "pykotaBalance" : str(user.AccountBalance or 0.0), 956 938 "pykotaOverCharge" : str(user.OverCharge), 957 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 958 } 959 else : 939 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 940 } 941 else : 960 942 fields = { self.info["userrdn"] : uname, 961 943 "objectClass" : ["pykotaObject", "pykotaAccount"], 962 944 "cn" : uname, 963 } 964 fields.update(newfields) 945 } 946 fields.update(newfields) 965 947 dn = "%s=%s,%s" % (self.info["userrdn"], uname, self.info["userbase"]) 966 948 self.doAdd(dn, fields) 967 if self.info["userbase"] != self.info["balancebase"] : 949 if self.info["userbase"] != self.info["balancebase"] : 968 950 fields = { self.info["balancerdn"] : uname, 969 951 "objectClass" : ["pykotaObject", "pykotaAccountBalance"], … … 971 953 "pykotaBalance" : str(user.AccountBalance or 0.0), 972 954 "pykotaOverCharge" : str(user.OverCharge), 973 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 974 } 955 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 956 } 975 957 dn = "%s=%s,%s" % (self.info["balancerdn"], uname, self.info["balancebase"]) 976 958 self.doAdd(dn, fields) … … 982 964 user.isDirty = False 983 965 return None # the entry created doesn't need further modification 984 985 def addGroup(self, group) : 966 967 def addGroup(self, group) : 986 968 """Adds a group to the quota storage, returns the old value if it already exists.""" 987 969 oldentry = self.getGroup(group.Name) … … 989 971 return oldentry # we return the existing entry 990 972 gname = self.userCharsetToDatabase(group.Name) 991 newfields = { 973 newfields = { 992 974 "pykotaGroupName" : gname, 993 975 "pykotaLimitBy" : (group.LimitBy or "quota"), 994 976 "description" : self.userCharsetToDatabase(group.Description or "") 995 } 977 } 996 978 mustadd = 1 997 979 if self.info["newgroup"].lower() != 'below' : … … 1013 995 else : 1014 996 message = _("Unable to find an existing entry to attach pykotaGroup objectclass %s") % group.Name 1015 if action.lower() == "warn" : 997 if action.lower() == "warn" : 1016 998 self.tool.printInfo("%s. A new entry will be created instead." % message, "warn") 1017 999 else : # 'fail' or incorrect setting 1018 1000 raise PyKotaStorageError, "%s. Action aborted. Please check your configuration." % message 1019 1001 1020 1002 if mustadd : 1021 1003 fields = { self.info["grouprdn"] : gname, 1022 1004 "objectClass" : ["pykotaObject", "pykotaGroup"], 1023 1005 "cn" : gname, 1024 } 1025 fields.update(newfields) 1006 } 1007 fields.update(newfields) 1026 1008 dn = "%s=%s,%s" % (self.info["grouprdn"], gname, self.info["groupbase"]) 1027 1009 self.doAdd(dn, fields) 1028 1010 group.isDirty = False 1029 1011 return None # the entry created doesn't need further modification 1030 1031 def addUserToGroup(self, user, group) : 1012 1013 def addUserToGroup(self, user, group) : 1032 1014 """Adds an user to a group.""" 1033 1015 if user.Name not in [u.Name for u in self.getGroupMembers(group)] : 1034 result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE) 1016 result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE) 1035 1017 if result : 1036 1018 fields = result[0][1] … … 1040 1022 self.doModify(group.ident, fields) 1041 1023 group.Members.append(user) 1042 1043 def delUserFromGroup(self, user, group) : 1024 1025 def delUserFromGroup(self, user, group) : 1044 1026 """Removes an user from a group.""" 1045 1027 if user.Name in [u.Name for u in self.getGroupMembers(group)] : … … 1049 1031 if not fields.has_key(self.info["groupmembers"]) : 1050 1032 fields[self.info["groupmembers"]] = [] 1051 try : 1033 try : 1052 1034 fields[self.info["groupmembers"]].remove(self.userCharsetToDatabase(user.Name)) 1053 1035 except ValueError : … … 1056 1038 self.doModify(group.ident, fields) 1057 1039 group.Members.remove(user) 1058 1040 1059 1041 def addUserPQuota(self, upq) : 1060 1042 """Initializes a user print quota on a printer.""" … … 1077 1059 "pykotaWarnCount" : str(upq.WarnCount or 0), 1078 1060 "pykotaMaxJobSize" : str(upq.MaxJobSize or 0), 1079 } 1061 } 1080 1062 if self.info["userquotabase"].lower() == "user" : 1081 1063 dn = "cn=%s,%s" % (uuid, upq.User.ident) 1082 else : 1064 else : 1083 1065 dn = "cn=%s,%s" % (uuid, self.info["userquotabase"]) 1084 1066 self.doAdd(dn, fields) 1085 1067 upq.isDirty = False 1086 1068 return None # the entry created doesn't need further modification 1087 1069 1088 1070 def addGroupPQuota(self, gpq) : 1089 1071 """Initializes a group print quota on a printer.""" … … 1099 1081 "pykotaPrinterName" : pname, 1100 1082 "pykotaDateLimit" : "None", 1101 } 1083 } 1102 1084 if self.info["groupquotabase"].lower() == "group" : 1103 1085 dn = "cn=%s,%s" % (uuid, gpq.Group.ident) 1104 else : 1086 else : 1105 1087 dn = "cn=%s,%s" % (uuid, self.info["groupquotabase"]) 1106 1088 self.doAdd(dn, fields) 1107 1089 gpq.isDirty = False 1108 1090 return None # the entry created doesn't need further modification 1109 1110 def savePrinter(self, printer) : 1091 1092 def savePrinter(self, printer) : 1111 1093 """Saves the printer to the database in a single operation.""" 1112 1094 fields = { … … 1118 1100 } 1119 1101 self.doModify(printer.ident, fields) 1120 1102 1121 1103 def saveUser(self, user) : 1122 1104 """Saves the user to the database in a single operation.""" 1123 1105 newfields = { 1124 1106 "pykotaLimitBy" : (user.LimitBy or "quota"), 1125 "description" : self.userCharsetToDatabase(user.Description or ""), 1107 "description" : self.userCharsetToDatabase(user.Description or ""), 1126 1108 self.info["usermail"] : user.Email or "", 1127 } 1109 } 1128 1110 self.doModify(user.ident, newfields) 1129 1111 1130 1112 newfields = { "pykotaBalance" : str(user.AccountBalance or 0.0), 1131 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 1113 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 1132 1114 "pykotaOverCharge" : str(user.OverCharge), 1133 1115 } 1134 1116 self.doModify(user.idbalance, newfields) 1135 1117 1136 1118 def saveGroup(self, group) : 1137 1119 """Saves the group to the database in a single operation.""" 1138 1120 newfields = { 1139 1121 "pykotaLimitBy" : (group.LimitBy or "quota"), 1140 "description" : self.userCharsetToDatabase(group.Description or ""), 1141 } 1122 "description" : self.userCharsetToDatabase(group.Description or ""), 1123 } 1142 1124 self.doModify(group.ident, newfields) 1143 1144 def writeUserPQuotaDateLimit(self, userpquota, datelimit) : 1125 1126 def writeUserPQuotaDateLimit(self, userpquota, datelimit) : 1145 1127 """Sets the date limit permanently for a user print quota.""" 1146 1128 fields = { … … 1148 1130 } 1149 1131 return self.doModify(userpquota.ident, fields) 1150 1151 def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) : 1132 1133 def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) : 1152 1134 """Sets the date limit permanently for a group print quota.""" 1153 1135 fields = { … … 1155 1137 } 1156 1138 return self.doModify(grouppquota.ident, fields) 1157 1158 def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) : 1139 1140 def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) : 1159 1141 """Increase page counters for a user print quota.""" 1160 1142 fields = { … … 1162 1144 "pykotaLifePageCounter" : { "operator" : "+", "value" : nbpages, "convert" : int }, 1163 1145 } 1164 return self.doModify(userpquota.ident, fields) 1165 1166 def decreaseUserAccountBalance(self, user, amount) : 1146 return self.doModify(userpquota.ident, fields) 1147 1148 def decreaseUserAccountBalance(self, user, amount) : 1167 1149 """Decreases user's account balance from an amount.""" 1168 1150 fields = { 1169 1151 "pykotaBalance" : { "operator" : "-", "value" : amount, "convert" : float }, 1170 1152 } 1171 return self.doModify(user.idbalance, fields, flushcache=1) 1172 1153 return self.doModify(user.idbalance, fields, flushcache=1) 1154 1173 1155 def writeNewPayment(self, user, amount, comment="") : 1174 1156 """Adds a new payment to the payments history.""" … … 1180 1162 "pykotaPayments" : payments, 1181 1163 } 1182 return self.doModify(user.idbalance, fields) 1183 1184 def writeLastJobSize(self, lastjob, jobsize, jobprice) : 1164 return self.doModify(user.idbalance, fields) 1165 1166 def writeLastJobSize(self, lastjob, jobsize, jobprice) : 1185 1167 """Sets the last job's size permanently.""" 1186 1168 fields = { … … 1188 1170 "pykotaJobPrice" : str(jobprice), 1189 1171 } 1190 self.doModify(lastjob.ident, fields) 1191 1172 self.doModify(lastjob.ident, fields) 1173 1192 1174 def writeJobNew(self, printer, user, jobid, pagecounter, action, jobsize=None, jobprice=None, filename=None, title=None, copies=None, options=None, clienthost=None, jobsizebytes=None, jobmd5sum=None, jobpages=None, jobbilling=None, precomputedsize=None, precomputedprice=None) : 1193 1175 """Adds a job in a printer's history.""" … … 1197 1179 uuid = self.genUUID() 1198 1180 dn = "cn=%s,%s" % (uuid, self.info["jobbase"]) 1199 else : 1181 else : 1200 1182 uuid = printer.LastJob.ident[3:].split(",")[0] 1201 1183 dn = printer.LastJob.ident 1202 if self.privacy : 1184 if self.privacy : 1203 1185 # For legal reasons, we want to hide the title, filename and options 1204 1186 title = filename = options = "hidden" … … 1211 1193 "pykotaPrinterPageCounter" : str(pagecounter), 1212 1194 "pykotaAction" : action, 1213 "pykotaFileName" : ((filename is None) and "None") or self.userCharsetToDatabase(filename), 1214 "pykotaTitle" : ((title is None) and "None") or self.userCharsetToDatabase(title), 1215 "pykotaCopies" : str(copies), 1216 "pykotaOptions" : ((options is None) and "None") or self.userCharsetToDatabase(options), 1217 "pykotaHostName" : str(clienthost), 1195 "pykotaFileName" : ((filename is None) and "None") or self.userCharsetToDatabase(filename), 1196 "pykotaTitle" : ((title is None) and "None") or self.userCharsetToDatabase(title), 1197 "pykotaCopies" : str(copies), 1198 "pykotaOptions" : ((options is None) and "None") or self.userCharsetToDatabase(options), 1199 "pykotaHostName" : str(clienthost), 1218 1200 "pykotaJobSizeBytes" : str(jobsizebytes), 1219 1201 "pykotaMD5Sum" : str(jobmd5sum), … … 1224 1206 } 1225 1207 if (not self.disablehistory) or (not printer.LastJob.Exists) : 1226 if jobsize is not None : 1208 if jobsize is not None : 1227 1209 fields.update({ "pykotaJobSize" : str(jobsize), "pykotaJobPrice" : str(jobprice) }) 1228 1210 self.doAdd(dn, fields) 1229 else : 1211 else : 1230 1212 # here we explicitly want to reset jobsize to 'None' if needed 1231 1213 fields.update({ "pykotaJobSize" : str(jobsize), "pykotaJobPrice" : str(jobprice) }) 1232 1214 self.doModify(dn, fields) 1233 1215 1234 1216 if printer.LastJob.Exists : 1235 1217 fields = { 1236 1218 "pykotaLastJobIdent" : uuid, 1237 1219 } 1238 self.doModify(printer.LastJob.lastjobident, fields) 1239 else : 1220 self.doModify(printer.LastJob.lastjobident, fields) 1221 else : 1240 1222 lastjuuid = self.genUUID() 1241 1223 lastjdn = "cn=%s,%s" % (lastjuuid, self.info["lastjobbase"]) … … 1245 1227 "pykotaPrinterName" : pname, 1246 1228 "pykotaLastJobIdent" : uuid, 1247 } 1248 self.doAdd(lastjdn, fields) 1249 1229 } 1230 self.doAdd(lastjdn, fields) 1231 1250 1232 def saveUserPQuota(self, userpquota) : 1251 1233 """Saves an user print quota entry.""" 1252 fields = { 1234 fields = { 1253 1235 "pykotaSoftLimit" : str(userpquota.SoftLimit), 1254 1236 "pykotaHardLimit" : str(userpquota.HardLimit), … … 1260 1242 } 1261 1243 self.doModify(userpquota.ident, fields) 1262 1244 1263 1245 def writeUserPQuotaWarnCount(self, userpquota, warncount) : 1264 1246 """Sets the warn counter value for a user quota.""" 1265 fields = { 1247 fields = { 1266 1248 "pykotaWarnCount" : str(warncount or 0), 1267 1249 } 1268 1250 self.doModify(userpquota.ident, fields) 1269 1251 1270 1252 def increaseUserPQuotaWarnCount(self, userpquota) : 1271 1253 """Increases the warn counter value for a user quota.""" … … 1273 1255 "pykotaWarnCount" : { "operator" : "+", "value" : 1, "convert" : int }, 1274 1256 } 1275 return self.doModify(userpquota.ident, fields) 1276 1257 return self.doModify(userpquota.ident, fields) 1258 1277 1259 def saveGroupPQuota(self, grouppquota) : 1278 1260 """Saves a group print quota entry.""" 1279 fields = { 1261 fields = { 1280 1262 "pykotaSoftLimit" : str(grouppquota.SoftLimit), 1281 1263 "pykotaHardLimit" : str(grouppquota.HardLimit), … … 1284 1266 } 1285 1267 self.doModify(grouppquota.ident, fields) 1286 1268 1287 1269 def writePrinterToGroup(self, pgroup, printer) : 1288 1270 """Puts a printer into a printer group.""" … … 1291 1273 fields = { 1292 1274 "uniqueMember" : pgroup.uniqueMember 1293 } 1294 self.doModify(pgroup.ident, fields) 1295 1275 } 1276 self.doModify(pgroup.ident, fields) 1277 1296 1278 def removePrinterFromGroup(self, pgroup, printer) : 1297 1279 """Removes a printer from a printer group.""" 1298 1280 try : 1299 1281 pgroup.uniqueMember.remove(printer.ident) 1300 except ValueError : 1282 except ValueError : 1301 1283 pass 1302 else : 1284 else : 1303 1285 fields = { 1304 1286 "uniqueMember" : pgroup.uniqueMember, 1305 } 1306 self.doModify(pgroup.ident, fields) 1307 1287 } 1288 self.doModify(pgroup.ident, fields) 1289 1308 1290 def retrieveHistory(self, user=None, printer=None, hostname=None, billingcode=None, jobid=None, limit=100, start=None, end=None) : 1309 1291 """Retrieves all print jobs for user on printer (or all) between start and end date, limited to first 100 results.""" … … 1320 1302 if jobid is not None : 1321 1303 where.append("(pykotaJobId=%s)" % jobid) # TODO : jobid is text, so self.userCharsetToDatabase(jobid) but do all of them as well. 1322 if where : 1304 if where : 1323 1305 where = "(&%s)" % "".join([precond] + where) 1324 else : 1306 else : 1325 1307 where = precond 1326 jobs = [] 1327 result = self.doSearch(where, fields=[ "pykotaJobSizeBytes", 1328 "pykotaHostName", 1329 "pykotaUserName", 1330 "pykotaPrinterName", 1331 "pykotaJobId", 1332 "pykotaPrinterPageCounter", 1333 "pykotaAction", 1334 "pykotaJobSize", 1335 "pykotaJobPrice", 1336 "pykotaFileName", 1337 "pykotaTitle", 1338 "pykotaCopies", 1339 "pykotaOptions", 1340 "pykotaBillingCode", 1341 "pykotaPages", 1342 "pykotaMD5Sum", 1308 jobs = [] 1309 result = self.doSearch(where, fields=[ "pykotaJobSizeBytes", 1310 "pykotaHostName", 1311 "pykotaUserName", 1312 "pykotaPrinterName", 1313 "pykotaJobId", 1314 "pykotaPrinterPageCounter", 1315 "pykotaAction", 1316 "pykotaJobSize", 1317 "pykotaJobPrice", 1318 "pykotaFileName", 1319 "pykotaTitle", 1320 "pykotaCopies", 1321 "pykotaOptions", 1322 "pykotaBillingCode", 1323 "pykotaPages", 1324 "pykotaMD5Sum", 1343 1325 "pykotaPrecomputedJobSize", 1344 1326 "pykotaPrecomputedJobPrice", 1345 "createTimestamp" ], 1327 "createTimestamp" ], 1346 1328 base=self.info["jobbase"]) 1347 1329 if result : … … 1353 1335 try : 1354 1336 job.JobSize = int(fields.get("pykotaJobSize", [0])[0]) 1355 except ValueError : 1337 except ValueError : 1356 1338 job.JobSize = None 1357 try : 1339 try : 1358 1340 job.JobPrice = float(fields.get("pykotaJobPrice", [0.0])[0]) 1359 1341 except ValueError : 1360 1342 job.JobPrice = None 1361 1343 job.JobAction = fields.get("pykotaAction", [""])[0] 1362 job.JobFileName = self.databaseToUserCharset(fields.get("pykotaFileName", [""])[0]) 1363 job.JobTitle = self.databaseToUserCharset(fields.get("pykotaTitle", [""])[0]) 1344 job.JobFileName = self.databaseToUserCharset(fields.get("pykotaFileName", [""])[0]) 1345 job.JobTitle = self.databaseToUserCharset(fields.get("pykotaTitle", [""])[0]) 1364 1346 job.JobCopies = int(fields.get("pykotaCopies", [0])[0]) 1365 job.JobOptions = self.databaseToUserCharset(fields.get("pykotaOptions", [""])[0]) 1347 job.JobOptions = self.databaseToUserCharset(fields.get("pykotaOptions", [""])[0]) 1366 1348 job.JobHostName = fields.get("pykotaHostName", [""])[0] 1367 1349 job.JobSizeBytes = fields.get("pykotaJobSizeBytes", [0L])[0] … … 1371 1353 try : 1372 1354 job.PrecomputedJobSize = int(fields.get("pykotaPrecomputedJobSize", [0])[0]) 1373 except ValueError : 1355 except ValueError : 1374 1356 job.PrecomputedJobSize = None 1375 try : 1357 try : 1376 1358 job.PrecomputedJobPrice = float(fields.get("pykotaPrecomputedJobPrice", [0.0])[0]) 1377 1359 except ValueError : … … 1390 1372 job.Exists = True 1391 1373 jobs.append(job) 1392 jobs.sort(lambda x, y : cmp(y.JobDate, x.JobDate)) 1393 if limit : 1374 jobs.sort(lambda x, y : cmp(y.JobDate, x.JobDate)) 1375 if limit : 1394 1376 jobs = jobs[:int(limit)] 1395 1377 return jobs 1396 1397 def deleteUser(self, user) : 1378 1379 def deleteUser(self, user) : 1398 1380 """Completely deletes an user from the Quota Storage.""" 1399 1381 uname = self.userCharsetToDatabase(user.Name) 1400 todelete = [] 1382 todelete = [] 1401 1383 result = self.doSearch("(&(objectClass=pykotaJob)(pykotaUserName=%s))" % uname, base=self.info["jobbase"]) 1402 1384 for (ident, fields) in result : … … 1412 1394 # ensure the user print quota entry will be deleted 1413 1395 todelete.append(ident) 1414 1396 1415 1397 # if last job of current printer was printed by the user 1416 1398 # to delete, we also need to delete the printer's last job entry. … … 1418 1400 if printer.LastJob.UserName == user.Name : 1419 1401 todelete.append(printer.LastJob.lastjobident) 1420 1421 for ident in todelete : 1402 1403 for ident in todelete : 1422 1404 self.doDelete(ident) 1423 1424 result = self.doSearch("objectClass=pykotaAccount", None, base=user.ident, scope=ldap.SCOPE_BASE) 1405 1406 result = self.doSearch("objectClass=pykotaAccount", None, base=user.ident, scope=ldap.SCOPE_BASE) 1425 1407 if result : 1426 1408 fields = result[0][1] … … 1428 1410 if k.startswith("pykota") : 1429 1411 del fields[k] 1430 elif k.lower() == "objectclass" : 1412 elif k.lower() == "objectclass" : 1431 1413 todelete = [] 1432 1414 for i in range(len(fields[k])) : 1433 if fields[k][i].startswith("pykota") : 1415 if fields[k][i].startswith("pykota") : 1434 1416 todelete.append(i) 1435 todelete.sort() 1417 todelete.sort() 1436 1418 todelete.reverse() 1437 1419 for i in todelete : 1438 1420 del fields[k][i] 1439 1421 if fields.get("objectClass") or fields.get("objectclass") : 1440 self.doModify(user.ident, fields, ignoreold=0) 1441 else : 1422 self.doModify(user.ident, fields, ignoreold=0) 1423 else : 1442 1424 self.doDelete(user.ident) 1443 1425 result = self.doSearch("(&(objectClass=pykotaAccountBalance)(pykotaUserName=%s))" % \ … … 1447 1429 for (ident, fields) in result : 1448 1430 self.doDelete(ident) 1449 1450 def deleteGroup(self, group) : 1431 1432 def deleteGroup(self, group) : 1451 1433 """Completely deletes a group from the Quota Storage.""" 1452 1434 gname = self.userCharsetToDatabase(group.Name) … … 1461 1443 for (ident, fields) in result : 1462 1444 self.doDelete(ident) 1463 result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE) 1445 result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE) 1464 1446 if result : 1465 1447 fields = result[0][1] … … 1467 1449 if k.startswith("pykota") : 1468 1450 del fields[k] 1469 elif k.lower() == "objectclass" : 1451 elif k.lower() == "objectclass" : 1470 1452 todelete = [] 1471 1453 for i in range(len(fields[k])) : 1472 if fields[k][i].startswith("pykota") : 1454 if fields[k][i].startswith("pykota") : 1473 1455 todelete.append(i) 1474 todelete.sort() 1456 todelete.sort() 1475 1457 todelete.reverse() 1476 1458 for i in todelete : 1477 1459 del fields[k][i] 1478 1460 if fields.get("objectClass") or fields.get("objectclass") : 1479 self.doModify(group.ident, fields, ignoreold=0) 1480 else : 1461 self.doModify(group.ident, fields, ignoreold=0) 1462 else : 1481 1463 self.doDelete(group.ident) 1482 1464 1483 1465 def deleteManyBillingCodes(self, billingcodes) : 1484 1466 """Deletes many billing codes.""" 1485 1467 for bcode in billingcodes : 1486 1468 bcode.delete() 1487 1488 def deleteManyUsers(self, users) : 1469 1470 def deleteManyUsers(self, users) : 1489 1471 """Deletes many users.""" 1490 1472 for user in users : 1491 1473 user.delete() 1492 1493 def deleteManyGroups(self, groups) : 1474 1475 def deleteManyGroups(self, groups) : 1494 1476 """Deletes many groups.""" 1495 1477 for group in groups : 1496 1478 group.delete() 1497 1498 def deleteManyPrinters(self, printers) : 1479 1480 def deleteManyPrinters(self, printers) : 1499 1481 """Deletes many printers.""" 1500 1482 for printer in printers : 1501 1483 printer.delete() 1502 1503 def deleteManyUserPQuotas(self, printers, users) : 1484 1485 def deleteManyUserPQuotas(self, printers, users) : 1504 1486 """Deletes many user print quota entries.""" 1505 1487 # TODO : grab all with a single (possibly VERY huge) filter if possible (might depend on the LDAP server !) … … 1509 1491 if upq.Exists : 1510 1492 upq.delete() 1511 1493 1512 1494 def deleteManyGroupPQuotas(self, printers, groups) : 1513 1495 """Deletes many group print quota entries.""" … … 1518 1500 if gpq.Exists : 1519 1501 gpq.delete() 1520 1521 def deleteUserPQuota(self, upquota) : 1502 1503 def deleteUserPQuota(self, upquota) : 1522 1504 """Completely deletes an user print quota entry from the database.""" 1523 1505 uname = self.userCharsetToDatabase(upquota.User.Name) … … 1531 1513 self.doDelete(upquota.Printer.LastJob.lastjobident) 1532 1514 self.doDelete(upquota.ident) 1533 1534 def deleteGroupPQuota(self, gpquota) : 1515 1516 def deleteGroupPQuota(self, gpquota) : 1535 1517 """Completely deletes a group print quota entry from the database.""" 1536 1518 self.doDelete(gpquota.ident) 1537 1538 def deletePrinter(self, printer) : 1519 1520 def deletePrinter(self, printer) : 1539 1521 """Completely deletes a printer from the Quota Storage.""" 1540 1522 pname = self.userCharsetToDatabase(printer.Name) … … 1559 1541 for (ident, fields) in result : 1560 1542 self.doDelete(ident) 1561 for parent in self.getParentPrinters(printer) : 1543 for parent in self.getParentPrinters(printer) : 1562 1544 try : 1563 1545 parent.uniqueMember.remove(printer.ident) 1564 except ValueError : 1546 except ValueError : 1565 1547 pass 1566 else : 1548 else : 1567 1549 fields = { 1568 1550 "uniqueMember" : parent.uniqueMember, 1569 } 1570 self.doModify(parent.ident, fields) 1571 self.doDelete(printer.ident) 1572 1551 } 1552 self.doModify(parent.ident, fields) 1553 self.doDelete(printer.ident) 1554 1573 1555 def deleteBillingCode(self, code) : 1574 1556 """Deletes a billing code from the Quota Storage (no entries are deleted from the history)""" 1575 1557 self.doDelete(code.ident) 1576 1577 def sortRecords(self, fields, records, default, ordering) : 1558 1559 def sortRecords(self, fields, records, default, ordering) : 1578 1560 """Sort records based on list of fields prefixed with '+' (ASC) or '-' (DESC).""" 1579 1561 fieldindexes = {} 1580 1562 for i in range(len(fields)) : 1581 1563 fieldindexes[fields[i]] = i 1582 if not ordering : 1564 if not ordering : 1583 1565 ordering = default 1584 orderby = [] 1566 orderby = [] 1585 1567 for orderkey in ordering : 1586 1568 # Create ordering hints, ignoring unknown fields … … 1593 1575 if index is not None : 1594 1576 orderby.append((+1, index)) 1595 else : 1577 else : 1596 1578 index = fieldindexes.get(orderkey) 1597 1579 if index is not None : 1598 1580 orderby.append((+1, index)) 1599 1600 def compare(x, y, orderby=orderby) : 1581 1582 def compare(x, y, orderby=orderby) : 1601 1583 """Compares two records.""" 1602 1584 i = 0 … … 1607 1589 if not result : 1608 1590 i += 1 1609 else : 1591 else : 1610 1592 return sign * result 1611 return 0 # identical keys 1612 1593 return 0 # identical keys 1594 1613 1595 records.sort(compare) 1614 1596 return records 1615 1597 1616 1598 def extractPrinters(self, extractonly={}, ordering=[]) : 1617 1599 """Extracts all printer records.""" … … 1624 1606 if entry.PassThrough in (1, "1", "t", "true", "T", "TRUE", "True") : 1625 1607 passthrough = "t" 1626 else : 1608 else : 1627 1609 passthrough = "f" 1628 1610 result.append((entry.ident, entry.Name, entry.PricePerPage, entry.PricePerJob, entry.Description, entry.MaxJobSize, passthrough)) 1629 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1630 1611 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1612 1631 1613 def extractUsers(self, extractonly={}, ordering=[]) : 1632 1614 """Extracts all user records.""" … … 1639 1621 result.append((entry.ident, entry.Name, entry.AccountBalance, entry.LifeTimePaid, entry.LimitBy, entry.Email, entry.Description, entry.OverCharge)) 1640 1622 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1641 1623 1642 1624 def extractBillingcodes(self, extractonly={}, ordering=[]) : 1643 1625 """Extracts all billing codes records.""" … … 1650 1632 result.append((entry.ident, entry.BillingCode, entry.Balance, entry.PageCounter, entry.Description)) 1651 1633 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1652 1634 1653 1635 def extractGroups(self, extractonly={}, ordering=[]) : 1654 1636 """Extracts all group records.""" … … 1661 1643 result.append((entry.ident, entry.Name, entry.LimitBy, entry.AccountBalance, entry.LifeTimePaid, entry.Description)) 1662 1644 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1663 1645 1664 1646 def extractPayments(self, extractonly={}, ordering=[]) : 1665 1647 """Extracts all payment records.""" … … 1680 1662 result.append((entry.Name, amount, date, description)) 1681 1663 return [fields] + self.sortRecords(fields, result, ["+date"], ordering) 1682 1664 1683 1665 def extractUpquotas(self, extractonly={}, ordering=[]) : 1684 1666 """Extracts all userpquota records.""" … … 1693 1675 result.append((user.Name, entry.Name, userpquota.ident, user.ident, entry.ident, userpquota.LifePageCounter, userpquota.PageCounter, userpquota.SoftLimit, userpquota.HardLimit, userpquota.DateLimit)) 1694 1676 return [fields] + self.sortRecords(fields, result, ["+userdn"], ordering) 1695 1677 1696 1678 def extractGpquotas(self, extractonly={}, ordering=[]) : 1697 1679 """Extracts all grouppquota records.""" … … 1706 1688 result.append((group.Name, entry.Name, grouppquota.ident, group.ident, entry.ident, grouppquota.LifePageCounter, grouppquota.PageCounter, grouppquota.SoftLimit, grouppquota.HardLimit, grouppquota.DateLimit)) 1707 1689 return [fields] + self.sortRecords(fields, result, ["+groupdn"], ordering) 1708 1690 1709 1691 def extractUmembers(self, extractonly={}, ordering=[]) : 1710 1692 """Extracts all user groups members.""" … … 1720 1702 result.append((entry.Name, member.Name, entry.ident, member.ident)) 1721 1703 return [fields] + self.sortRecords(fields, result, ["+groupdn", "+userdn"], ordering) 1722 1704 1723 1705 def extractPmembers(self, extractonly={}, ordering=[]) : 1724 1706 """Extracts all printer groups members.""" … … 1734 1716 result.append((parent.Name, entry.Name, parent.ident, entry.ident)) 1735 1717 return [fields] + self.sortRecords(fields, result, ["+pgroupdn", "+printerdn"], ordering) 1736 1718 1737 1719 def extractHistory(self, extractonly={}, ordering=[]) : 1738 1720 """Extracts all jobhistory records.""" … … 1740 1722 if uname : 1741 1723 user = self.getUser(uname) 1742 else : 1724 else : 1743 1725 user = None 1744 1726 pname = extractonly.get("printername") 1745 1727 if pname : 1746 1728 printer = self.getPrinter(pname) 1747 else : 1729 else : 1748 1730 printer = None 1749 1731 startdate = extractonly.get("start") … … 1755 1737 result = [] 1756 1738 for entry in entries : 1757 result.append((entry.UserName, entry.PrinterName, entry.ident, entry.JobId, entry.PrinterPageCounter, entry.JobSize, entry.JobAction, entry.JobDate, entry.JobFileName, entry.JobTitle, entry.JobCopies, entry.JobOptions, entry.JobPrice, entry.JobHostName, entry.JobSizeBytes, entry.JobMD5Sum, entry.JobPages, entry.JobBillingCode, entry.PrecomputedJobSize, entry.PrecomputedJobPrice)) 1739 result.append((entry.UserName, entry.PrinterName, entry.ident, entry.JobId, entry.PrinterPageCounter, entry.JobSize, entry.JobAction, entry.JobDate, entry.JobFileName, entry.JobTitle, entry.JobCopies, entry.JobOptions, entry.JobPrice, entry.JobHostName, entry.JobSizeBytes, entry.JobMD5Sum, entry.JobPages, entry.JobBillingCode, entry.PrecomputedJobSize, entry.PrecomputedJobPrice)) 1758 1740 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1759 1741 1760 1742 def getBillingCodeFromBackend(self, label) : 1761 1743 """Extracts billing code information given its label : returns first matching billing code.""" … … 1772 1754 code.PageCounter = int(fields.get("pykotaPageCounter", [0])[0]) 1773 1755 code.Balance = float(fields.get("pykotaBalance", [0.0])[0]) 1774 code.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 1756 code.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 1775 1757 code.Exists = True 1776 return code 1777 1758 return code 1759 1778 1760 def addBillingCode(self, bcode) : 1779 1761 """Adds a billing code to the quota storage, returns it.""" … … 1788 1770 "pykotaPageCounter" : str(bcode.PageCounter or 0), 1789 1771 "pykotaBalance" : str(bcode.Balance or 0.0), 1790 "description" : self.userCharsetToDatabase(bcode.Description or ""), 1791 } 1772 "description" : self.userCharsetToDatabase(bcode.Description or ""), 1773 } 1792 1774 self.doAdd(dn, fields) 1793 1775 bcode.isDirty = False 1794 1776 return None # the entry created doesn't need further modification 1795 1777 1796 1778 def saveBillingCode(self, bcode) : 1797 1779 """Sets the new description for a billing code.""" 1798 1780 fields = { 1799 "description" : self.userCharsetToDatabase(bcode.Description or ""), 1781 "description" : self.userCharsetToDatabase(bcode.Description or ""), 1800 1782 "pykotaPageCounter" : str(bcode.PageCounter or 0), 1801 1783 "pykotaBalance" : str(bcode.Balance or 0.0), 1802 1784 } 1803 1785 self.doModify(bcode.ident, fields) 1804 1786 1805 1787 def getMatchingBillingCodes(self, billingcodepattern) : 1806 1788 """Returns the list of all billing codes which match a certain pattern.""" … … 1811 1793 if result : 1812 1794 patterns = billingcodepattern.split(",") 1813 try : 1814 patdict = {}.fromkeys(patterns) 1815 except AttributeError : 1816 # Python v2.2 or earlier 1817 patdict = {} 1818 for p in patterns : 1819 patdict[p] = None 1795 patdict = {}.fromkeys(patterns) 1820 1796 for (codeid, fields) in result : 1821 1797 codename = self.databaseToUserCharset(fields.get("pykotaBillingCode", [""])[0]) … … 1825 1801 code.PageCounter = int(fields.get("pykotaPageCounter", [0])[0]) 1826 1802 code.Balance = float(fields.get("pykotaBalance", [0.0])[0]) 1827 code.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 1803 code.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 1828 1804 code.Exists = True 1829 1805 codes.append(code) 1830 1806 self.cacheEntry("BILLINGCODES", code.BillingCode, code) 1831 return codes 1832 1807 return codes 1808 1833 1809 def consumeBillingCode(self, bcode, pagecounter, balance) : 1834 1810 """Consumes from a billing code.""" … … 1837 1813 "pykotaPageCounter" : { "operator" : "+", "value" : pagecounter, "convert" : int }, 1838 1814 } 1839 return self.doModify(bcode.ident, fields) 1840 1841 def refundJob(self, jobident) : 1815 return self.doModify(bcode.ident, fields) 1816 1817 def refundJob(self, jobident) : 1842 1818 """Marks a job as refunded in the history.""" 1843 1819 dn = "cn=%s,%s" % (ident, self.info["jobbase"]) 1844 1820 fields = { 1845 1821 "pykotaAction" : "REFUND", 1846 } 1847 self.doModify(dn, fields) 1848 1822 } 1823 self.doModify(dn, fields) 1824 1849 1825 def storageUserFromRecord(self, username, record) : 1850 1826 """Returns a StorageUser instance from a database record.""" … … 1852 1828 user.Exists = True 1853 1829 return user 1854 1830 1855 1831 def storageGroupFromRecord(self, groupname, record) : 1856 1832 """Returns a StorageGroup instance from a database record.""" … … 1858 1834 group.Exists = True 1859 1835 return group 1860 1836 1861 1837 def storagePrinterFromRecord(self, printername, record) : 1862 1838 """Returns a StoragePrinter instance from a database record.""" … … 1864 1840 printer.Exists = True 1865 1841 return printer 1866 1867 def setJobAttributesFromRecord(self, job, record) : 1842 1843 def setJobAttributesFromRecord(self, job, record) : 1868 1844 """Sets the attributes of a job from a database record.""" 1869 1845 job.Exists = True 1870 1846 1871 1847 def storageJobFromRecord(self, record) : 1872 1848 """Returns a StorageJob instance from a database record.""" … … 1874 1850 self.setJobAttributesFromRecord(job, record) 1875 1851 return job 1876 1852 1877 1853 def storageLastJobFromRecord(self, printer, record) : 1878 1854 """Returns a StorageLastJob instance from a database record.""" … … 1880 1856 self.setJobAttributesFromRecord(lastjob, record) 1881 1857 return lastjob 1882 1858 1883 1859 def storageUserPQuotaFromRecord(self, user, printer, record) : 1884 1860 """Returns a StorageUserPQuota instance from a database record.""" … … 1886 1862 userpquota.Exists = True 1887 1863 return userpquota 1888 1864 1889 1865 def storageGroupPQuotaFromRecord(self, group, printer, record) : 1890 1866 """Returns a StorageGroupPQuota instance from a database record.""" … … 1892 1868 grouppquota.Exists = True 1893 1869 return grouppquota 1894 1870 1895 1871 def storageBillingCodeFromRecord(self, billingcode, record) : 1896 1872 """Returns a StorageBillingCode instance from a database record."""