Changeset 3413 for pykota/trunk/pykota/storages/ldapstorage.py
- Timestamp:
- 09/27/08 22:02:37 (16 years ago)
- Files:
-
- 1 modified
Legend:
- Unmodified
- Added
- Removed
-
pykota/trunk/pykota/storages/ldapstorage.py
r3411 r3413 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 23 23 """This module defines a class to access to an LDAP database backend. 24 24 25 My IANA assigned number, for 26 "Conseil Internet & Logiciels Libres, Jerome Alet" 25 My IANA assigned number, for 26 "Conseil Internet & Logiciels Libres, Jerome Alet" 27 27 is 16868. Use this as a base to extend the LDAP schema. 28 28 """ … … 42 42 StorageJob, StorageLastJob, StorageUserPQuota, \ 43 43 StorageGroupPQuota, StorageBillingCode 44 45 from pykota.utils import * 44 45 from pykota.utils import * 46 46 47 47 try : 48 48 import ldap 49 49 import ldap.modlist 50 except ImportError : 50 except ImportError : 51 51 raise PyKotaStorageError, "This python version (%s) doesn't seem to have the python-ldap module installed correctly." % sys.version.split()[0] 52 else : 52 else : 53 53 try : 54 54 from ldap.cidict import cidict 55 except ImportError : 55 except ImportError : 56 56 import UserDict 57 57 sys.stderr.write("ERROR: PyKota requires a newer version of python-ldap. Workaround activated. Please upgrade python-ldap !\n") 58 58 class cidict(UserDict.UserDict) : 59 59 pass # Fake it all, and don't care for case insensitivity : users who need it will have to upgrade. 60 60 61 61 class Storage(BaseStorage) : 62 62 def __init__(self, pykotatool, host, dbname, user, passwd) : … … 68 68 self.savedpasswd = passwd 69 69 self.secondStageInit() 70 71 def secondStageInit(self) : 70 71 def secondStageInit(self) : 72 72 """Second stage initialisation.""" 73 73 BaseStorage.__init__(self, self.savedtool) … … 77 77 try : 78 78 self.tool.logdebug("Trying to open database (host=%s, dbname=%s, user=%s)..." % (self.savedhost, self.saveddbname, self.saveduser)) 79 self.database = ldap.initialize(self.savedhost) 79 self.database = ldap.initialize(self.savedhost) 80 80 if self.info["ldaptls"] : 81 81 # we want TLS … … 85 85 self.database.simple_bind_s(self.saveduser, self.savedpasswd) 86 86 self.basedn = self.saveddbname 87 except ldap.SERVER_DOWN : 87 except ldap.SERVER_DOWN : 88 88 message = "LDAP backend for PyKota seems to be down !" 89 89 self.tool.printInfo("%s" % message, "error") 90 90 self.tool.printInfo("Trying again in 2 seconds...", "warn") 91 91 time.sleep(2) 92 except ldap.LDAPError : 92 except ldap.LDAPError : 93 93 message = "Unable to connect to LDAP server %s as %s." % (self.savedhost, self.saveduser) 94 94 self.tool.printInfo("%s" % message, "error") 95 95 self.tool.printInfo("Trying again in 2 seconds...", "warn") 96 96 time.sleep(2) 97 else : 97 else : 98 98 self.useldapcache = self.tool.config.getLDAPCache() 99 99 if self.useldapcache : … … 103 103 self.tool.logdebug("Database opened (host=%s, dbname=%s, user=%s)" % (self.savedhost, self.saveddbname, self.saveduser)) 104 104 return # All is fine here. 105 raise PyKotaStorageError, message 106 107 def close(self) : 105 raise PyKotaStorageError, message 106 107 def close(self) : 108 108 """Closes the database connection.""" 109 109 if not self.closed : … … 111 111 self.closed = 1 112 112 self.tool.logdebug("Database closed.") 113 114 def genUUID(self) : 113 114 def genUUID(self) : 115 115 """Generates an unique identifier. 116 116 117 117 TODO : this one is not unique accross several print servers, but should be sufficient for testing. 118 118 """ 119 119 return md5.md5("%s-%s" % (time.time(), random.random())).hexdigest() 120 121 def normalizeFields(self, fields) : 120 121 def normalizeFields(self, fields) : 122 122 """Ensure all items are lists.""" 123 123 for (k, v) in fields.items() : … … 125 125 if not v : 126 126 del fields[k] 127 else : 127 else : 128 128 fields[k] = [ v ] 129 return fields 130 131 def beginTransaction(self) : 129 return fields 130 131 def beginTransaction(self) : 132 132 """Starts a transaction.""" 133 133 self.tool.logdebug("Transaction begins... WARNING : No transactions in LDAP !") 134 135 def commitTransaction(self) : 134 135 def commitTransaction(self) : 136 136 """Commits a transaction.""" 137 137 self.tool.logdebug("Transaction committed. WARNING : No transactions in LDAP !") 138 139 def rollbackTransaction(self) : 138 139 def rollbackTransaction(self) : 140 140 """Rollbacks a transaction.""" 141 141 self.tool.logdebug("Transaction aborted. WARNING : No transaction in LDAP !") 142 142 143 143 def doSearch(self, key, fields=None, base="", scope=ldap.SCOPE_SUBTREE, flushcache=0) : 144 144 """Does an LDAP search query.""" … … 151 151 # retrieve ALL user defined attributes ("*") 152 152 # + the createTimestamp attribute, needed by job history 153 # 153 # 154 154 # This may not work with all LDAP servers 155 # but works at least in OpenLDAP (2.1.25) 155 # but works at least in OpenLDAP (2.1.25) 156 156 # and iPlanet Directory Server (5.1 SP3) 157 fields = ["*", "createTimestamp"] 158 157 fields = ["*", "createTimestamp"] 158 159 159 if self.useldapcache and (not flushcache) and (scope == ldap.SCOPE_BASE) and self.ldapcache.has_key(base) : 160 160 entry = self.ldapcache[base] … … 164 164 self.querydebug("QUERY : Filter : %s, BaseDN : %s, Scope : %s, Attributes : %s" % (key, base, scope, fields)) 165 165 result = self.database.search_s(base, scope, key, fields) 166 except ldap.NO_SUCH_OBJECT, msg : 166 except ldap.NO_SUCH_OBJECT, msg : 167 167 raise PyKotaStorageError, (_("Search base %s doesn't seem to exist. Probable misconfiguration. Please double check /etc/pykota/pykota.conf : %s") % (base, msg)) 168 except ldap.LDAPError, msg : 168 except ldap.LDAPError, msg : 169 169 message = (_("Search for %s(%s) from %s(scope=%s) returned no answer.") % (key, fields, base, scope)) + " : %s" % msg 170 170 self.tool.printInfo("LDAP error : %s" % message, "error") … … 172 172 self.close() 173 173 self.secondStageInit() 174 else : 174 else : 175 175 self.querydebug("QUERY : Result : %s" % result) 176 176 result = [ (dn, cidict(attrs)) for (dn, attrs) in result ] … … 181 181 return result 182 182 raise PyKotaStorageError, message 183 183 184 184 def doAdd(self, dn, fields) : 185 185 """Adds an entry in the LDAP directory.""" … … 192 192 self.querydebug("%s" % entry) 193 193 self.database.add_s(dn, entry) 194 except ldap.ALREADY_EXISTS, msg : 194 except ldap.ALREADY_EXISTS, msg : 195 195 raise PyKotaStorageError, "Entry %s already exists : %s" % (dn, msg) 196 196 except ldap.LDAPError, msg : … … 206 206 return dn 207 207 raise PyKotaStorageError, message 208 208 209 209 def doDelete(self, dn) : 210 210 """Deletes an entry from the LDAP directory.""" … … 214 214 self.querydebug("QUERY : Delete(%s)" % dn) 215 215 self.database.delete_s(dn) 216 except ldap.NO_SUCH_OBJECT : 216 except ldap.NO_SUCH_OBJECT : 217 217 self.tool.printInfo("Entry %s was already missing before we deleted it. This **MAY** be normal." % dn, "info") 218 218 except ldap.LDAPError, msg : … … 222 222 self.close() 223 223 self.secondStageInit() 224 else : 224 else : 225 225 if self.useldapcache : 226 226 try : 227 227 self.querydebug("LDAP cache del %s" % dn) 228 228 del self.ldapcache[dn] 229 except KeyError : 229 except KeyError : 230 230 pass 231 return 231 return 232 232 raise PyKotaStorageError, message 233 233 234 234 def doModify(self, dn, fields, ignoreold=1, flushcache=0) : 235 235 """Modifies an entry in the LDAP directory.""" … … 246 246 if k != "createTimestamp" : 247 247 oldentry[k] = v 248 else : 248 else : 249 249 self.querydebug("LDAP cache miss %s" % dn) 250 250 oldentry = self.doSearch("objectClass=*", base=dn, scope=ldap.SCOPE_BASE)[0][1] 251 else : 251 else : 252 252 oldentry = self.doSearch("objectClass=*", base=dn, scope=ldap.SCOPE_BASE, flushcache=flushcache)[0][1] 253 253 for (k, v) in fields.items() : … … 255 255 try : 256 256 oldvalue = v["convert"](oldentry.get(k, [0])[0]) 257 except ValueError : 257 except ValueError : 258 258 self.querydebug("Error converting %s with %s(%s)" % (oldentry.get(k), k, v)) 259 259 oldvalue = 0 260 260 if v["operator"] == '+' : 261 261 newvalue = oldvalue + v["value"] 262 else : 262 else : 263 263 newvalue = oldvalue - v["value"] 264 264 fields[k] = str(newvalue) … … 288 288 try : 289 289 del cachedentry[mtyp] 290 except KeyError : 290 except KeyError : 291 291 pass 292 292 self.querydebug("LDAP cache update %s => %s" % (dn, cachedentry)) 293 293 return dn 294 294 raise PyKotaStorageError, message 295 295 296 296 def filterNames(self, records, attribute, patterns=None) : 297 297 """Returns a list of 'attribute' from a list of records. 298 298 299 299 Logs any missing attribute. 300 """ 300 """ 301 301 result = [] 302 302 for (dn, record) in records : … … 309 309 if (not isinstance(patterns, type([]))) and (not isinstance(patterns, type(()))) : 310 310 patterns = [ patterns ] 311 if self.tool.matchString(attrval, patterns) : 311 if self.tool.matchString(attrval, patterns) : 312 312 result.append(attrval) 313 else : 313 else : 314 314 result.append(attrval) 315 return result 316 317 def getAllBillingCodes(self, billingcode=None) : 315 return result 316 317 def getAllBillingCodes(self, billingcode=None) : 318 318 """Extracts all billing codes or only the billing codes matching the optional parameter.""" 319 319 ldapfilter = "objectClass=pykotaBilling" … … 321 321 if result : 322 322 return self.filterNames(result, "pykotaBillingCode", billingcode) 323 else : 323 else : 324 324 return [] 325 326 def getAllPrintersNames(self, printername=None) : 325 326 def getAllPrintersNames(self, printername=None) : 327 327 """Extracts all printer names or only the printers' names matching the optional parameter.""" 328 328 ldapfilter = "objectClass=pykotaPrinter" … … 330 330 if result : 331 331 return self.filterNames(result, "pykotaPrinterName", printername) 332 else : 332 else : 333 333 return [] 334 335 def getAllUsersNames(self, username=None) : 334 335 def getAllUsersNames(self, username=None) : 336 336 """Extracts all user names or only the users' names matching the optional parameter.""" 337 337 ldapfilter = "objectClass=pykotaAccount" … … 339 339 if result : 340 340 return self.filterNames(result, "pykotaUserName", username) 341 else : 341 else : 342 342 return [] 343 344 def getAllGroupsNames(self, groupname=None) : 343 344 def getAllGroupsNames(self, groupname=None) : 345 345 """Extracts all group names or only the groups' names matching the optional parameter.""" 346 346 ldapfilter = "objectClass=pykotaGroup" … … 348 348 if result : 349 349 return self.filterNames(result, "pykotaGroupName", groupname) 350 else : 350 else : 351 351 return [] 352 352 353 353 def getUserNbJobsFromHistory(self, user) : 354 354 """Returns the number of jobs the user has in history.""" 355 355 result = self.doSearch("(&(pykotaUserName=%s)(objectClass=pykotaJob))" % unicodeToDatabase(user.Name), None, base=self.info["jobbase"]) 356 356 return len(result) 357 358 def getUserFromBackend(self, username) : 357 358 def getUserFromBackend(self, username) : 359 359 """Extracts user information given its name.""" 360 360 user = StorageUser(self, username) … … 377 377 if user.AccountBalance[0].upper() == "NONE" : 378 378 user.AccountBalance = None 379 else : 379 else : 380 380 user.AccountBalance = float(user.AccountBalance[0]) 381 user.AccountBalance = user.AccountBalance or 0.0 381 user.AccountBalance = user.AccountBalance or 0.0 382 382 user.LifeTimePaid = fields.get("pykotaLifeTimePaid") 383 383 user.OverCharge = float(fields.get("pykotaOverCharge", [1.0])[0]) … … 385 385 if user.LifeTimePaid[0].upper() == "NONE" : 386 386 user.LifeTimePaid = None 387 else : 387 else : 388 388 user.LifeTimePaid = float(user.LifeTimePaid[0]) 389 user.LifeTimePaid = user.LifeTimePaid or 0.0 389 user.LifeTimePaid = user.LifeTimePaid or 0.0 390 390 user.Payments = [] 391 391 for payment in fields.get("pykotaPayments", []) : … … 396 396 (date, amount) = payment.split(" # ") 397 397 description = "" 398 else : 398 else : 399 399 description = databaseToUnicode(base64.decodestring(description)) 400 if amount.endswith(" #") : 400 if amount.endswith(" #") : 401 401 amount = amount[:-2] # TODO : should be catched earlier, the bug is above I think 402 402 user.Payments.append((date, float(amount), description)) 403 403 user.Exists = True 404 404 return user 405 406 def getGroupFromBackend(self, groupname) : 405 406 def getGroupFromBackend(self, groupname) : 407 407 """Extracts group information given its name.""" 408 408 group = StorageGroup(self, groupname) … … 423 423 group.Exists = True 424 424 return group 425 426 def getPrinterFromBackend(self, printername) : 425 426 def getPrinterFromBackend(self, printername) : 427 427 """Extracts printer information given its name : returns first matching printer.""" 428 428 printer = StoragePrinter(self, printername) … … 447 447 printer.PassThrough = 0 448 448 printer.uniqueMember = fields.get("uniqueMember", []) 449 printer.Description = databaseToUnicode(fields.get("description", [""])[0]) 449 printer.Description = databaseToUnicode(fields.get("description", [""])[0]) 450 450 printer.Exists = True 451 return printer 452 453 def getUserPQuotaFromBackend(self, user, printer) : 451 return printer 452 453 def getUserPQuotaFromBackend(self, user, printer) : 454 454 """Extracts a user print quota.""" 455 455 userpquota = StorageUserPQuota(self, user, printer) … … 457 457 if self.info["userquotabase"].lower() == "user" : 458 458 base = user.ident 459 else : 459 else : 460 460 base = self.info["userquotabase"] 461 461 result = self.doSearch("(&(objectClass=pykotaUserPQuota)(pykotaUserName=%s)(pykotaPrinterName=%s))" % \ … … 473 473 if userpquota.SoftLimit[0].upper() == "NONE" : 474 474 userpquota.SoftLimit = None 475 else : 475 else : 476 476 userpquota.SoftLimit = int(userpquota.SoftLimit[0]) 477 477 userpquota.HardLimit = fields.get("pykotaHardLimit") … … 479 479 if userpquota.HardLimit[0].upper() == "NONE" : 480 480 userpquota.HardLimit = None 481 elif userpquota.HardLimit is not None : 481 elif userpquota.HardLimit is not None : 482 482 userpquota.HardLimit = int(userpquota.HardLimit[0]) 483 483 userpquota.DateLimit = fields.get("pykotaDateLimit") 484 484 if userpquota.DateLimit is not None : 485 if userpquota.DateLimit[0].upper() == "NONE" : 485 if userpquota.DateLimit[0].upper() == "NONE" : 486 486 userpquota.DateLimit = None 487 else : 487 else : 488 488 userpquota.DateLimit = userpquota.DateLimit[0] 489 489 userpquota.MaxJobSize = fields.get("pykotaMaxJobSize") … … 491 491 if userpquota.MaxJobSize[0].upper() == "NONE" : 492 492 userpquota.MaxJobSize = None 493 else : 493 else : 494 494 userpquota.MaxJobSize = int(userpquota.MaxJobSize[0]) 495 495 userpquota.Exists = True 496 496 return userpquota 497 498 def getGroupPQuotaFromBackend(self, group, printer) : 497 498 def getGroupPQuotaFromBackend(self, group, printer) : 499 499 """Extracts a group print quota.""" 500 500 grouppquota = StorageGroupPQuota(self, group, printer) … … 502 502 if self.info["groupquotabase"].lower() == "group" : 503 503 base = group.ident 504 else : 504 else : 505 505 base = self.info["groupquotabase"] 506 506 result = self.doSearch("(&(objectClass=pykotaGroupPQuota)(pykotaGroupName=%s)(pykotaPrinterName=%s))" % \ … … 515 515 if grouppquota.SoftLimit[0].upper() == "NONE" : 516 516 grouppquota.SoftLimit = None 517 else : 517 else : 518 518 grouppquota.SoftLimit = int(grouppquota.SoftLimit[0]) 519 519 grouppquota.HardLimit = fields.get("pykotaHardLimit") … … 521 521 if grouppquota.HardLimit[0].upper() == "NONE" : 522 522 grouppquota.HardLimit = None 523 else : 523 else : 524 524 grouppquota.HardLimit = int(grouppquota.HardLimit[0]) 525 525 grouppquota.DateLimit = fields.get("pykotaDateLimit") 526 526 if grouppquota.DateLimit is not None : 527 if grouppquota.DateLimit[0].upper() == "NONE" : 527 if grouppquota.DateLimit[0].upper() == "NONE" : 528 528 grouppquota.DateLimit = None 529 else : 529 else : 530 530 grouppquota.DateLimit = grouppquota.DateLimit[0] 531 531 grouppquota.MaxJobSize = fields.get("pykotaMaxJobSize") … … 533 533 if grouppquota.MaxJobSize[0].upper() == "NONE" : 534 534 grouppquota.MaxJobSize = None 535 else : 535 else : 536 536 grouppquota.MaxJobSize = int(grouppquota.MaxJobSize[0]) 537 537 grouppquota.PageCounter = 0 … … 548 548 ["pykotaPageCounter", "pykotaLifePageCounter"], base=base) 549 549 if result : 550 for userpquota in result : 550 for userpquota in result : 551 551 grouppquota.PageCounter += int(userpquota[1].get("pykotaPageCounter", [0])[0] or 0) 552 552 grouppquota.LifePageCounter += int(userpquota[1].get("pykotaLifePageCounter", [0])[0] or 0) 553 553 grouppquota.Exists = True 554 554 return grouppquota 555 556 def getPrinterLastJobFromBackend(self, printer) : 555 556 def getPrinterLastJobFromBackend(self, printer) : 557 557 """Extracts a printer's last job information.""" 558 558 lastjob = StorageLastJob(self, printer) … … 567 567 result = None 568 568 try : 569 result = self.doSearch("objectClass=pykotaJob", [ "pykotaJobSizeBytes", 570 "pykotaHostName", 571 "pykotaUserName", 572 "pykotaPrinterName", 573 "pykotaJobId", 574 "pykotaPrinterPageCounter", 575 "pykotaJobSize", 576 "pykotaAction", 577 "pykotaJobPrice", 578 "pykotaFileName", 579 "pykotaTitle", 580 "pykotaCopies", 581 "pykotaOptions", 582 "pykotaBillingCode", 583 "pykotaPages", 584 "pykotaMD5Sum", 569 result = self.doSearch("objectClass=pykotaJob", [ "pykotaJobSizeBytes", 570 "pykotaHostName", 571 "pykotaUserName", 572 "pykotaPrinterName", 573 "pykotaJobId", 574 "pykotaPrinterPageCounter", 575 "pykotaJobSize", 576 "pykotaAction", 577 "pykotaJobPrice", 578 "pykotaFileName", 579 "pykotaTitle", 580 "pykotaCopies", 581 "pykotaOptions", 582 "pykotaBillingCode", 583 "pykotaPages", 584 "pykotaMD5Sum", 585 585 "pykotaPrecomputedJobSize", 586 586 "pykotaPrecomputedJobPrice", 587 "createTimestamp" ], 587 "createTimestamp" ], 588 588 base="cn=%s,%s" % (lastjobident, self.info["jobbase"]), scope=ldap.SCOPE_BASE) 589 except PyKotaStorageError : 590 pass # Last job entry exists, but job probably doesn't exist anymore. 589 except PyKotaStorageError : 590 pass # Last job entry exists, but job probably doesn't exist anymore. 591 591 if result : 592 592 fields = result[0][1] … … 597 597 try : 598 598 lastjob.JobSize = int(fields.get("pykotaJobSize", [0])[0]) 599 except ValueError : 599 except ValueError : 600 600 lastjob.JobSize = None 601 try : 601 try : 602 602 lastjob.JobPrice = float(fields.get("pykotaJobPrice", [0.0])[0]) 603 except ValueError : 603 except ValueError : 604 604 lastjob.JobPrice = None 605 605 lastjob.JobAction = databaseToUnicode(fields.get("pykotaAction", [""])[0]) 606 lastjob.JobFileName = databaseToUnicode(fields.get("pykotaFileName", [""])[0]) 607 lastjob.JobTitle = databaseToUnicode(fields.get("pykotaTitle", [""])[0]) 606 lastjob.JobFileName = databaseToUnicode(fields.get("pykotaFileName", [""])[0]) 607 lastjob.JobTitle = databaseToUnicode(fields.get("pykotaTitle", [""])[0]) 608 608 lastjob.JobCopies = int(fields.get("pykotaCopies", [0])[0]) 609 lastjob.JobOptions = databaseToUnicode(fields.get("pykotaOptions", [""])[0]) 609 lastjob.JobOptions = databaseToUnicode(fields.get("pykotaOptions", [""])[0]) 610 610 lastjob.JobHostName = databaseToUnicode(fields.get("pykotaHostName", [""])[0]) 611 611 lastjob.JobSizeBytes = fields.get("pykotaJobSizeBytes", [0L])[0] … … 615 615 try : 616 616 lastjob.PrecomputedJobSize = int(fields.get("pykotaPrecomputedJobSize", [0])[0]) 617 except ValueError : 617 except ValueError : 618 618 lastjob.PrecomputedJobSize = None 619 try : 619 try : 620 620 lastjob.PrecomputedJobPrice = float(fields.get("pykotaPrecomputedJobPrice", [0.0])[0]) 621 except ValueError : 621 except ValueError : 622 622 lastjob.PrecomputedJobPrice = None 623 623 if lastjob.JobTitle == lastjob.JobFileName == lastjob.JobOptions == u"hidden" : … … 628 628 lastjob.Exists = True 629 629 return lastjob 630 631 def getGroupMembersFromBackend(self, group) : 630 631 def getGroupMembersFromBackend(self, group) : 632 632 """Returns the group's members list.""" 633 633 groupmembers = [] … … 640 640 for username in result[0][1].get(self.info["groupmembers"], []) : 641 641 groupmembers.append(self.getUser(databaseToUnicode(username))) 642 return groupmembers 643 644 def getUserGroupsFromBackend(self, user) : 642 return groupmembers 643 644 def getUserGroupsFromBackend(self, user) : 645 645 """Returns the user's groups list.""" 646 646 groups = [] … … 660 660 if group.LimitBy is not None : 661 661 group.LimitBy = databaseToUnicode(group.LimitBy[0]) 662 else : 662 else : 663 663 group.LimitBy = u"quota" 664 664 group.AccountBalance = 0.0 … … 671 671 self.cacheEntry("GROUPS", group.Name, group) 672 672 groups.append(group) 673 return groups 674 675 def getParentPrintersFromBackend(self, printer) : 673 return groups 674 675 def getParentPrintersFromBackend(self, printer) : 676 676 """Get all the printer groups this printer is a member of.""" 677 677 pgroups = [] … … 687 687 pgroups.append(parentprinter) 688 688 return pgroups 689 689 690 690 def getMatchingPrinters(self, printerpattern) : 691 691 """Returns the list of all printers for which name matches a certain pattern.""" … … 699 699 try : 700 700 patdict = {}.fromkeys(patterns) 701 except AttributeError : 701 except AttributeError : 702 702 # Python v2.2 or earlier 703 703 patdict = {} … … 718 718 printer.PassThrough = 0 719 719 printer.uniqueMember = fields.get("uniqueMember", []) 720 printer.Description = databaseToUnicode(fields.get("description", [""])[0]) 720 printer.Description = databaseToUnicode(fields.get("description", [""])[0]) 721 721 printer.Exists = True 722 722 printers.append(printer) 723 723 self.cacheEntry("PRINTERS", printer.Name, printer) 724 return printers 725 724 return printers 725 726 726 def getMatchingUsers(self, userpattern) : 727 727 """Returns the list of all users for which name matches a certain pattern.""" … … 735 735 try : 736 736 patdict = {}.fromkeys(patterns) 737 except AttributeError : 737 except AttributeError : 738 738 # Python v2.2 or earlier 739 739 patdict = {} … … 747 747 user.Email = databaseToUnicode(fields.get(self.info["usermail"], [None])[0]) 748 748 user.LimitBy = databaseToUnicode(fields.get("pykotaLimitBy", ["quota"])[0]) 749 user.Description = databaseToUnicode(fields.get("description", [""])[0]) 749 user.Description = databaseToUnicode(fields.get("description", [""])[0]) 750 750 uname = unicodeToDatabase(username) 751 751 result = self.doSearch("(&(objectClass=pykotaAccountBalance)(|(pykotaUserName=%s)(%s=%s)))" % \ … … 763 763 if user.AccountBalance[0].upper() == "NONE" : 764 764 user.AccountBalance = None 765 else : 765 else : 766 766 user.AccountBalance = float(user.AccountBalance[0]) 767 user.AccountBalance = user.AccountBalance or 0.0 767 user.AccountBalance = user.AccountBalance or 0.0 768 768 user.LifeTimePaid = fields.get("pykotaLifeTimePaid") 769 769 if user.LifeTimePaid is not None : 770 770 if user.LifeTimePaid[0].upper() == "NONE" : 771 771 user.LifeTimePaid = None 772 else : 772 else : 773 773 user.LifeTimePaid = float(user.LifeTimePaid[0]) 774 user.LifeTimePaid = user.LifeTimePaid or 0.0 774 user.LifeTimePaid = user.LifeTimePaid or 0.0 775 775 user.Payments = [] 776 776 for payment in fields.get("pykotaPayments", []) : … … 781 781 (date, amount) = payment.split(" # ") 782 782 description = "" 783 else : 783 else : 784 784 description = databaseToUnicode(base64.decodestring(description)) 785 if amount.endswith(" #") : 785 if amount.endswith(" #") : 786 786 amount = amount[:-2] # TODO : should be catched earlier, the bug is above I think 787 787 user.Payments.append((date, float(amount), description)) … … 789 789 users.append(user) 790 790 self.cacheEntry("USERS", user.Name, user) 791 return users 792 791 return users 792 793 793 def getMatchingGroups(self, grouppattern) : 794 794 """Returns the list of all groups for which name matches a certain pattern.""" … … 802 802 try : 803 803 patdict = {}.fromkeys(patterns) 804 except AttributeError : 804 except AttributeError : 805 805 # Python v2.2 or earlier 806 806 patdict = {} … … 812 812 group = StorageGroup(self, groupname) 813 813 group.ident = groupid 814 group.Name = databaseToUnicode(fields.get("pykotaGroupName", [groupname])[0]) 814 group.Name = databaseToUnicode(fields.get("pykotaGroupName", [groupname])[0]) 815 815 group.LimitBy = databaseToUnicode(fields.get("pykotaLimitBy", ["quota"])[0]) 816 group.Description = databaseToUnicode(fields.get("description", [""])[0]) 816 group.Description = databaseToUnicode(fields.get("description", [""])[0]) 817 817 group.AccountBalance = 0.0 818 818 group.LifeTimePaid = 0.0 … … 825 825 self.cacheEntry("GROUPS", group.Name, group) 826 826 return groups 827 828 def getPrinterUsersAndQuotas(self, printer, names=["*"]) : 827 828 def getPrinterUsersAndQuotas(self, printer, names=["*"]) : 829 829 """Returns the list of users who uses a given printer, along with their quotas.""" 830 830 usersandquotas = [] … … 851 851 if userpquota.SoftLimit[0].upper() == "NONE" : 852 852 userpquota.SoftLimit = None 853 else : 853 else : 854 854 userpquota.SoftLimit = int(userpquota.SoftLimit[0]) 855 855 userpquota.HardLimit = fields.get("pykotaHardLimit") … … 857 857 if userpquota.HardLimit[0].upper() == "NONE" : 858 858 userpquota.HardLimit = None 859 elif userpquota.HardLimit is not None : 859 elif userpquota.HardLimit is not None : 860 860 userpquota.HardLimit = int(userpquota.HardLimit[0]) 861 861 userpquota.DateLimit = fields.get("pykotaDateLimit") 862 862 if userpquota.DateLimit is not None : 863 if userpquota.DateLimit[0].upper() == "NONE" : 863 if userpquota.DateLimit[0].upper() == "NONE" : 864 864 userpquota.DateLimit = None 865 else : 865 else : 866 866 userpquota.DateLimit = userpquota.DateLimit[0] 867 867 userpquota.Exists = True 868 868 usersandquotas.append((user, userpquota)) 869 869 self.cacheEntry("USERPQUOTAS", "%s@%s" % (user.Name, printer.Name), userpquota) 870 usersandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name)) 870 usersandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name)) 871 871 return usersandquotas 872 873 def getPrinterGroupsAndQuotas(self, printer, names=["*"]) : 872 873 def getPrinterGroupsAndQuotas(self, printer, names=["*"]) : 874 874 """Returns the list of groups which uses a given printer, along with their quotas.""" 875 875 groupsandquotas = [] … … 889 889 grouppquota = self.getGroupPQuota(group, printer) 890 890 groupsandquotas.append((group, grouppquota)) 891 groupsandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name)) 891 groupsandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name)) 892 892 return groupsandquotas 893 893 894 894 def addPrinter(self, printer) : 895 895 """Adds a printer to the quota storage, returns the old value if it already exists.""" … … 907 907 "pykotaPricePerPage" : str(printer.PricePerPage or 0.0), 908 908 "pykotaPricePerJob" : str(printer.PricePerJob or 0.0), 909 } 909 } 910 910 dn = "%s=%s,%s" % (self.info["printerrdn"], printername, self.info["printerbase"]) 911 911 self.doAdd(dn, fields) 912 912 printer.isDirty = False 913 913 return None # the entry created doesn't need further modification 914 915 def addUser(self, user) : 914 915 def addUser(self, user) : 916 916 """Adds a user to the quota storage, returns the old value if it already exists.""" 917 917 oldentry = self.getUser(user.Name) … … 924 924 "description" : unicodeToDatabase(user.Description or ""), 925 925 self.info["usermail"] : unicodeToDatabase(user.Email or ""), 926 } 927 926 } 927 928 928 mustadd = 1 929 929 if self.info["newuser"].lower() != 'below' : … … 943 943 fields.update({ "pykotaBalance" : str(user.AccountBalance or 0.0), 944 944 "pykotaOverCharge" : str(user.OverCharge), 945 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), }) 945 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), }) 946 946 self.doModify(dn, fields) 947 947 mustadd = 0 948 948 else : 949 949 message = _("Unable to find an existing objectClass %s entry with %s=%s to attach pykotaAccount objectClass") % (where, self.info["userrdn"], user.Name) 950 if action.lower() == "warn" : 950 if action.lower() == "warn" : 951 951 self.tool.printInfo(_("%s. A new entry will be created instead.") % message, "warn") 952 952 else : # 'fail' or incorrect setting 953 953 raise PyKotaStorageError, "%s. Action aborted. Please check your configuration." % message 954 954 955 955 if mustadd : 956 if self.info["userbase"] == self.info["balancebase"] : 956 if self.info["userbase"] == self.info["balancebase"] : 957 957 fields = { self.info["userrdn"] : uname, 958 958 "objectClass" : ["pykotaObject", "pykotaAccount", "pykotaAccountBalance"], … … 960 960 "pykotaBalance" : str(user.AccountBalance or 0.0), 961 961 "pykotaOverCharge" : str(user.OverCharge), 962 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 963 } 964 else : 962 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 963 } 964 else : 965 965 fields = { self.info["userrdn"] : uname, 966 966 "objectClass" : ["pykotaObject", "pykotaAccount"], 967 967 "cn" : uname, 968 } 969 fields.update(newfields) 968 } 969 fields.update(newfields) 970 970 dn = "%s=%s,%s" % (self.info["userrdn"], uname, self.info["userbase"]) 971 971 self.doAdd(dn, fields) 972 if self.info["userbase"] != self.info["balancebase"] : 972 if self.info["userbase"] != self.info["balancebase"] : 973 973 fields = { self.info["balancerdn"] : uname, 974 974 "objectClass" : ["pykotaObject", "pykotaAccountBalance"], … … 976 976 "pykotaBalance" : str(user.AccountBalance or 0.0), 977 977 "pykotaOverCharge" : str(user.OverCharge), 978 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 979 } 978 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 979 } 980 980 dn = "%s=%s,%s" % (self.info["balancerdn"], uname, self.info["balancebase"]) 981 981 self.doAdd(dn, fields) … … 987 987 user.isDirty = False 988 988 return None # the entry created doesn't need further modification 989 990 def addGroup(self, group) : 989 990 def addGroup(self, group) : 991 991 """Adds a group to the quota storage, returns the old value if it already exists.""" 992 992 oldentry = self.getGroup(group.Name) … … 994 994 return oldentry # we return the existing entry 995 995 gname = unicodeToDatabase(group.Name) 996 newfields = { 996 newfields = { 997 997 "pykotaGroupName" : gname, 998 998 "pykotaLimitBy" : unicodeToDatabase(group.LimitBy or u"quota"), 999 999 "description" : unicodeToDatabase(group.Description or "") 1000 } 1000 } 1001 1001 mustadd = 1 1002 1002 if self.info["newgroup"].lower() != 'below' : … … 1018 1018 else : 1019 1019 message = _("Unable to find an existing entry to attach pykotaGroup objectclass %s") % group.Name 1020 if action.lower() == "warn" : 1020 if action.lower() == "warn" : 1021 1021 self.tool.printInfo("%s. A new entry will be created instead." % message, "warn") 1022 1022 else : # 'fail' or incorrect setting 1023 1023 raise PyKotaStorageError, "%s. Action aborted. Please check your configuration." % message 1024 1024 1025 1025 if mustadd : 1026 1026 fields = { self.info["grouprdn"] : gname, 1027 1027 "objectClass" : ["pykotaObject", "pykotaGroup"], 1028 1028 "cn" : gname, 1029 } 1030 fields.update(newfields) 1029 } 1030 fields.update(newfields) 1031 1031 dn = "%s=%s,%s" % (self.info["grouprdn"], gname, self.info["groupbase"]) 1032 1032 self.doAdd(dn, fields) 1033 1033 group.isDirty = False 1034 1034 return None # the entry created doesn't need further modification 1035 1036 def addUserToGroup(self, user, group) : 1035 1036 def addUserToGroup(self, user, group) : 1037 1037 """Adds an user to a group.""" 1038 1038 if user.Name not in [u.Name for u in self.getGroupMembers(group)] : 1039 result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE) 1039 result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE) 1040 1040 if result : 1041 1041 fields = result[0][1] … … 1045 1045 self.doModify(group.ident, fields) 1046 1046 group.Members.append(user) 1047 1048 def delUserFromGroup(self, user, group) : 1047 1048 def delUserFromGroup(self, user, group) : 1049 1049 """Removes an user from a group.""" 1050 1050 if user.Name in [u.Name for u in self.getGroupMembers(group)] : … … 1054 1054 if not fields.has_key(self.info["groupmembers"]) : 1055 1055 fields[self.info["groupmembers"]] = [] 1056 try : 1056 try : 1057 1057 fields[self.info["groupmembers"]].remove(unicodeToDatabase(user.Name)) 1058 1058 except ValueError : … … 1061 1061 self.doModify(group.ident, fields) 1062 1062 group.Members.remove(user) 1063 1063 1064 1064 def addUserPQuota(self, upq) : 1065 1065 """Initializes a user print quota on a printer.""" … … 1082 1082 "pykotaWarnCount" : str(upq.WarnCount or 0), 1083 1083 "pykotaMaxJobSize" : str(upq.MaxJobSize or 0), 1084 } 1084 } 1085 1085 if self.info["userquotabase"].lower() == "user" : 1086 1086 dn = "cn=%s,%s" % (uuid, upq.User.ident) 1087 else : 1087 else : 1088 1088 dn = "cn=%s,%s" % (uuid, self.info["userquotabase"]) 1089 1089 self.doAdd(dn, fields) 1090 1090 upq.isDirty = False 1091 1091 return None # the entry created doesn't need further modification 1092 1092 1093 1093 def addGroupPQuota(self, gpq) : 1094 1094 """Initializes a group print quota on a printer.""" … … 1104 1104 "pykotaPrinterName" : pname, 1105 1105 "pykotaDateLimit" : "None", 1106 } 1106 } 1107 1107 if self.info["groupquotabase"].lower() == "group" : 1108 1108 dn = "cn=%s,%s" % (uuid, gpq.Group.ident) 1109 else : 1109 else : 1110 1110 dn = "cn=%s,%s" % (uuid, self.info["groupquotabase"]) 1111 1111 self.doAdd(dn, fields) 1112 1112 gpq.isDirty = False 1113 1113 return None # the entry created doesn't need further modification 1114 1115 def savePrinter(self, printer) : 1114 1115 def savePrinter(self, printer) : 1116 1116 """Saves the printer to the database in a single operation.""" 1117 1117 fields = { … … 1123 1123 } 1124 1124 self.doModify(printer.ident, fields) 1125 1125 1126 1126 def saveUser(self, user) : 1127 1127 """Saves the user to the database in a single operation.""" 1128 1128 newfields = { 1129 1129 "pykotaLimitBy" : unicodeToDatabase(user.LimitBy or u"quota"), 1130 "description" : unicodeToDatabase(user.Description or ""), 1130 "description" : unicodeToDatabase(user.Description or ""), 1131 1131 self.info["usermail"] : user.Email or "", 1132 } 1132 } 1133 1133 self.doModify(user.ident, newfields) 1134 1134 1135 1135 newfields = { "pykotaBalance" : str(user.AccountBalance or 0.0), 1136 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 1136 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 1137 1137 "pykotaOverCharge" : str(user.OverCharge), 1138 1138 } 1139 1139 self.doModify(user.idbalance, newfields) 1140 1140 1141 1141 def saveGroup(self, group) : 1142 1142 """Saves the group to the database in a single operation.""" 1143 1143 newfields = { 1144 1144 "pykotaLimitBy" : unicodeToDatabase(group.LimitBy or u"quota"), 1145 "description" : unicodeToDatabase(group.Description or ""), 1146 } 1145 "description" : unicodeToDatabase(group.Description or ""), 1146 } 1147 1147 self.doModify(group.ident, newfields) 1148 1149 def writeUserPQuotaDateLimit(self, userpquota, datelimit) : 1148 1149 def writeUserPQuotaDateLimit(self, userpquota, datelimit) : 1150 1150 """Sets the date limit permanently for a user print quota.""" 1151 1151 fields = { … … 1153 1153 } 1154 1154 return self.doModify(userpquota.ident, fields) 1155 1156 def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) : 1155 1156 def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) : 1157 1157 """Sets the date limit permanently for a group print quota.""" 1158 1158 fields = { … … 1160 1160 } 1161 1161 return self.doModify(grouppquota.ident, fields) 1162 1163 def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) : 1162 1163 def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) : 1164 1164 """Increase page counters for a user print quota.""" 1165 1165 fields = { … … 1167 1167 "pykotaLifePageCounter" : { "operator" : "+", "value" : nbpages, "convert" : int }, 1168 1168 } 1169 return self.doModify(userpquota.ident, fields) 1170 1171 def decreaseUserAccountBalance(self, user, amount) : 1169 return self.doModify(userpquota.ident, fields) 1170 1171 def decreaseUserAccountBalance(self, user, amount) : 1172 1172 """Decreases user's account balance from an amount.""" 1173 1173 fields = { 1174 1174 "pykotaBalance" : { "operator" : "-", "value" : amount, "convert" : float }, 1175 1175 } 1176 return self.doModify(user.idbalance, fields, flushcache=1) 1177 1176 return self.doModify(user.idbalance, fields, flushcache=1) 1177 1178 1178 def writeNewPayment(self, user, amount, comment="") : 1179 1179 """Adds a new payment to the payments history.""" … … 1185 1185 "pykotaPayments" : payments, 1186 1186 } 1187 return self.doModify(user.idbalance, fields) 1188 1189 def writeLastJobSize(self, lastjob, jobsize, jobprice) : 1187 return self.doModify(user.idbalance, fields) 1188 1189 def writeLastJobSize(self, lastjob, jobsize, jobprice) : 1190 1190 """Sets the last job's size permanently.""" 1191 1191 fields = { … … 1193 1193 "pykotaJobPrice" : str(jobprice), 1194 1194 } 1195 self.doModify(lastjob.ident, fields) 1196 1195 self.doModify(lastjob.ident, fields) 1196 1197 1197 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) : 1198 1198 """Adds a job in a printer's history.""" … … 1202 1202 uuid = self.genUUID() 1203 1203 dn = "cn=%s,%s" % (uuid, self.info["jobbase"]) 1204 else : 1204 else : 1205 1205 uuid = printer.LastJob.ident[3:].split(",")[0] 1206 1206 dn = printer.LastJob.ident 1207 if self.privacy : 1207 if self.privacy : 1208 1208 # For legal reasons, we want to hide the title, filename and options 1209 1209 title = filename = options = u"hidden" … … 1216 1216 "pykotaPrinterPageCounter" : str(pagecounter), 1217 1217 "pykotaAction" : unicodeToDatabase(action), 1218 "pykotaFileName" : ((filename is None) and "None") or unicodeToDatabase(filename), 1219 "pykotaTitle" : ((title is None) and "None") or unicodeToDatabase(title), 1220 "pykotaCopies" : str(copies), 1221 "pykotaOptions" : ((options is None) and "None") or unicodeToDatabase(options), 1222 "pykotaHostName" : str(clienthost), 1218 "pykotaFileName" : ((filename is None) and "None") or unicodeToDatabase(filename), 1219 "pykotaTitle" : ((title is None) and "None") or unicodeToDatabase(title), 1220 "pykotaCopies" : str(copies), 1221 "pykotaOptions" : ((options is None) and "None") or unicodeToDatabase(options), 1222 "pykotaHostName" : str(clienthost), 1223 1223 "pykotaJobSizeBytes" : str(jobsizebytes), 1224 1224 "pykotaMD5Sum" : unicodeToDatabase(jobmd5sum), … … 1229 1229 } 1230 1230 if (not self.disablehistory) or (not printer.LastJob.Exists) : 1231 if jobsize is not None : 1231 if jobsize is not None : 1232 1232 fields.update({ "pykotaJobSize" : str(jobsize), "pykotaJobPrice" : str(jobprice) }) 1233 1233 self.doAdd(dn, fields) 1234 else : 1234 else : 1235 1235 # here we explicitly want to reset jobsize to 'None' if needed 1236 1236 fields.update({ "pykotaJobSize" : str(jobsize), "pykotaJobPrice" : str(jobprice) }) 1237 1237 self.doModify(dn, fields) 1238 1238 1239 1239 if printer.LastJob.Exists : 1240 1240 fields = { 1241 1241 "pykotaLastJobIdent" : uuid, 1242 1242 } 1243 self.doModify(printer.LastJob.lastjobident, fields) 1244 else : 1243 self.doModify(printer.LastJob.lastjobident, fields) 1244 else : 1245 1245 lastjuuid = self.genUUID() 1246 1246 lastjdn = "cn=%s,%s" % (lastjuuid, self.info["lastjobbase"]) … … 1250 1250 "pykotaPrinterName" : pname, 1251 1251 "pykotaLastJobIdent" : uuid, 1252 } 1253 self.doAdd(lastjdn, fields) 1254 1252 } 1253 self.doAdd(lastjdn, fields) 1254 1255 1255 def saveUserPQuota(self, userpquota) : 1256 1256 """Saves an user print quota entry.""" 1257 fields = { 1257 fields = { 1258 1258 "pykotaSoftLimit" : str(userpquota.SoftLimit), 1259 1259 "pykotaHardLimit" : str(userpquota.HardLimit), … … 1265 1265 } 1266 1266 self.doModify(userpquota.ident, fields) 1267 1267 1268 1268 def writeUserPQuotaWarnCount(self, userpquota, warncount) : 1269 1269 """Sets the warn counter value for a user quota.""" 1270 fields = { 1270 fields = { 1271 1271 "pykotaWarnCount" : str(warncount or 0), 1272 1272 } 1273 1273 self.doModify(userpquota.ident, fields) 1274 1274 1275 1275 def increaseUserPQuotaWarnCount(self, userpquota) : 1276 1276 """Increases the warn counter value for a user quota.""" … … 1278 1278 "pykotaWarnCount" : { "operator" : "+", "value" : 1, "convert" : int }, 1279 1279 } 1280 return self.doModify(userpquota.ident, fields) 1281 1280 return self.doModify(userpquota.ident, fields) 1281 1282 1282 def saveGroupPQuota(self, grouppquota) : 1283 1283 """Saves a group print quota entry.""" 1284 fields = { 1284 fields = { 1285 1285 "pykotaSoftLimit" : str(grouppquota.SoftLimit), 1286 1286 "pykotaHardLimit" : str(grouppquota.HardLimit), … … 1289 1289 } 1290 1290 self.doModify(grouppquota.ident, fields) 1291 1291 1292 1292 def writePrinterToGroup(self, pgroup, printer) : 1293 1293 """Puts a printer into a printer group.""" … … 1296 1296 fields = { 1297 1297 "uniqueMember" : pgroup.uniqueMember 1298 } 1299 self.doModify(pgroup.ident, fields) 1300 1298 } 1299 self.doModify(pgroup.ident, fields) 1300 1301 1301 def removePrinterFromGroup(self, pgroup, printer) : 1302 1302 """Removes a printer from a printer group.""" 1303 1303 try : 1304 1304 pgroup.uniqueMember.remove(printer.ident) 1305 except ValueError : 1305 except ValueError : 1306 1306 pass 1307 else : 1307 else : 1308 1308 fields = { 1309 1309 "uniqueMember" : pgroup.uniqueMember, 1310 } 1311 self.doModify(pgroup.ident, fields) 1312 1310 } 1311 self.doModify(pgroup.ident, fields) 1312 1313 1313 def retrieveHistory(self, user=None, printer=None, hostname=None, billingcode=None, jobid=None, limit=100, start=None, end=None) : 1314 1314 """Retrieves all print jobs for user on printer (or all) between start and end date, limited to first 100 results.""" … … 1325 1325 if jobid is not None : 1326 1326 where.append("(pykotaJobId=%s)" % jobid) # TODO : jobid is text, so unicodeToDatabase(jobid) but do all of them as well. 1327 if where : 1327 if where : 1328 1328 where = "(&%s)" % "".join([precond] + where) 1329 else : 1329 else : 1330 1330 where = precond 1331 jobs = [] 1332 result = self.doSearch(where, fields=[ "pykotaJobSizeBytes", 1333 "pykotaHostName", 1334 "pykotaUserName", 1335 "pykotaPrinterName", 1336 "pykotaJobId", 1337 "pykotaPrinterPageCounter", 1338 "pykotaAction", 1339 "pykotaJobSize", 1340 "pykotaJobPrice", 1341 "pykotaFileName", 1342 "pykotaTitle", 1343 "pykotaCopies", 1344 "pykotaOptions", 1345 "pykotaBillingCode", 1346 "pykotaPages", 1347 "pykotaMD5Sum", 1331 jobs = [] 1332 result = self.doSearch(where, fields=[ "pykotaJobSizeBytes", 1333 "pykotaHostName", 1334 "pykotaUserName", 1335 "pykotaPrinterName", 1336 "pykotaJobId", 1337 "pykotaPrinterPageCounter", 1338 "pykotaAction", 1339 "pykotaJobSize", 1340 "pykotaJobPrice", 1341 "pykotaFileName", 1342 "pykotaTitle", 1343 "pykotaCopies", 1344 "pykotaOptions", 1345 "pykotaBillingCode", 1346 "pykotaPages", 1347 "pykotaMD5Sum", 1348 1348 "pykotaPrecomputedJobSize", 1349 1349 "pykotaPrecomputedJobPrice", 1350 "createTimestamp" ], 1350 "createTimestamp" ], 1351 1351 base=self.info["jobbase"]) 1352 1352 if result : … … 1358 1358 try : 1359 1359 job.JobSize = int(fields.get("pykotaJobSize", [0])[0]) 1360 except ValueError : 1360 except ValueError : 1361 1361 job.JobSize = None 1362 try : 1362 try : 1363 1363 job.JobPrice = float(fields.get("pykotaJobPrice", [0.0])[0]) 1364 1364 except ValueError : 1365 1365 job.JobPrice = None 1366 1366 job.JobAction = databaseToUnicode(fields.get("pykotaAction", [""])[0]) 1367 job.JobFileName = databaseToUnicode(fields.get("pykotaFileName", [""])[0]) 1368 job.JobTitle = databaseToUnicode(fields.get("pykotaTitle", [""])[0]) 1367 job.JobFileName = databaseToUnicode(fields.get("pykotaFileName", [""])[0]) 1368 job.JobTitle = databaseToUnicode(fields.get("pykotaTitle", [""])[0]) 1369 1369 job.JobCopies = int(fields.get("pykotaCopies", [0])[0]) 1370 job.JobOptions = databaseToUnicode(fields.get("pykotaOptions", [""])[0]) 1370 job.JobOptions = databaseToUnicode(fields.get("pykotaOptions", [""])[0]) 1371 1371 job.JobHostName = databaseToUnicode(fields.get("pykotaHostName", [""])[0]) 1372 1372 job.JobSizeBytes = fields.get("pykotaJobSizeBytes", [0L])[0] … … 1376 1376 try : 1377 1377 job.PrecomputedJobSize = int(fields.get("pykotaPrecomputedJobSize", [0])[0]) 1378 except ValueError : 1378 except ValueError : 1379 1379 job.PrecomputedJobSize = None 1380 try : 1380 try : 1381 1381 job.PrecomputedJobPrice = float(fields.get("pykotaPrecomputedJobPrice", [0.0])[0]) 1382 1382 except ValueError : … … 1395 1395 job.Exists = True 1396 1396 jobs.append(job) 1397 jobs.sort(lambda x, y : cmp(y.JobDate, x.JobDate)) 1398 if limit : 1397 jobs.sort(lambda x, y : cmp(y.JobDate, x.JobDate)) 1398 if limit : 1399 1399 jobs = jobs[:int(limit)] 1400 1400 return jobs 1401 1402 def deleteUser(self, user) : 1401 1402 def deleteUser(self, user) : 1403 1403 """Completely deletes an user from the Quota Storage.""" 1404 1404 uname = unicodeToDatabase(user.Name) 1405 todelete = [] 1405 todelete = [] 1406 1406 result = self.doSearch("(&(objectClass=pykotaJob)(pykotaUserName=%s))" % uname, base=self.info["jobbase"]) 1407 1407 for (ident, fields) in result : … … 1417 1417 # ensure the user print quota entry will be deleted 1418 1418 todelete.append(ident) 1419 1419 1420 1420 # if last job of current printer was printed by the user 1421 1421 # to delete, we also need to delete the printer's last job entry. … … 1423 1423 if printer.LastJob.UserName == user.Name : 1424 1424 todelete.append(printer.LastJob.lastjobident) 1425 1426 for ident in todelete : 1425 1426 for ident in todelete : 1427 1427 self.doDelete(ident) 1428 1429 result = self.doSearch("objectClass=pykotaAccount", None, base=user.ident, scope=ldap.SCOPE_BASE) 1428 1429 result = self.doSearch("objectClass=pykotaAccount", None, base=user.ident, scope=ldap.SCOPE_BASE) 1430 1430 if result : 1431 1431 fields = result[0][1] … … 1433 1433 if k.startswith("pykota") : 1434 1434 del fields[k] 1435 elif k.lower() == "objectclass" : 1435 elif k.lower() == "objectclass" : 1436 1436 todelete = [] 1437 1437 for i in range(len(fields[k])) : 1438 if fields[k][i].startswith("pykota") : 1438 if fields[k][i].startswith("pykota") : 1439 1439 todelete.append(i) 1440 todelete.sort() 1440 todelete.sort() 1441 1441 todelete.reverse() 1442 1442 for i in todelete : 1443 1443 del fields[k][i] 1444 1444 if fields.get("objectClass") or fields.get("objectclass") : 1445 self.doModify(user.ident, fields, ignoreold=0) 1446 else : 1445 self.doModify(user.ident, fields, ignoreold=0) 1446 else : 1447 1447 self.doDelete(user.ident) 1448 1448 result = self.doSearch("(&(objectClass=pykotaAccountBalance)(pykotaUserName=%s))" % \ … … 1452 1452 for (ident, fields) in result : 1453 1453 self.doDelete(ident) 1454 1455 def deleteGroup(self, group) : 1454 1455 def deleteGroup(self, group) : 1456 1456 """Completely deletes a group from the Quota Storage.""" 1457 1457 gname = unicodeToDatabase(group.Name) … … 1466 1466 for (ident, fields) in result : 1467 1467 self.doDelete(ident) 1468 result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE) 1468 result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE) 1469 1469 if result : 1470 1470 fields = result[0][1] … … 1472 1472 if k.startswith("pykota") : 1473 1473 del fields[k] 1474 elif k.lower() == "objectclass" : 1474 elif k.lower() == "objectclass" : 1475 1475 todelete = [] 1476 1476 for i in range(len(fields[k])) : 1477 if fields[k][i].startswith("pykota") : 1477 if fields[k][i].startswith("pykota") : 1478 1478 todelete.append(i) 1479 todelete.sort() 1479 todelete.sort() 1480 1480 todelete.reverse() 1481 1481 for i in todelete : 1482 1482 del fields[k][i] 1483 1483 if fields.get("objectClass") or fields.get("objectclass") : 1484 self.doModify(group.ident, fields, ignoreold=0) 1485 else : 1484 self.doModify(group.ident, fields, ignoreold=0) 1485 else : 1486 1486 self.doDelete(group.ident) 1487 1487 1488 1488 def deleteManyBillingCodes(self, billingcodes) : 1489 1489 """Deletes many billing codes.""" 1490 1490 for bcode in billingcodes : 1491 1491 bcode.delete() 1492 1493 def deleteManyUsers(self, users) : 1492 1493 def deleteManyUsers(self, users) : 1494 1494 """Deletes many users.""" 1495 1495 for user in users : 1496 1496 user.delete() 1497 1498 def deleteManyGroups(self, groups) : 1497 1498 def deleteManyGroups(self, groups) : 1499 1499 """Deletes many groups.""" 1500 1500 for group in groups : 1501 1501 group.delete() 1502 1503 def deleteManyPrinters(self, printers) : 1502 1503 def deleteManyPrinters(self, printers) : 1504 1504 """Deletes many printers.""" 1505 1505 for printer in printers : 1506 1506 printer.delete() 1507 1508 def deleteManyUserPQuotas(self, printers, users) : 1507 1508 def deleteManyUserPQuotas(self, printers, users) : 1509 1509 """Deletes many user print quota entries.""" 1510 1510 # TODO : grab all with a single (possibly VERY huge) filter if possible (might depend on the LDAP server !) … … 1514 1514 if upq.Exists : 1515 1515 upq.delete() 1516 1516 1517 1517 def deleteManyGroupPQuotas(self, printers, groups) : 1518 1518 """Deletes many group print quota entries.""" … … 1523 1523 if gpq.Exists : 1524 1524 gpq.delete() 1525 1526 def deleteUserPQuota(self, upquota) : 1525 1526 def deleteUserPQuota(self, upquota) : 1527 1527 """Completely deletes an user print quota entry from the database.""" 1528 1528 uname = unicodeToDatabase(upquota.User.Name) … … 1536 1536 self.doDelete(upquota.Printer.LastJob.lastjobident) 1537 1537 self.doDelete(upquota.ident) 1538 1539 def deleteGroupPQuota(self, gpquota) : 1538 1539 def deleteGroupPQuota(self, gpquota) : 1540 1540 """Completely deletes a group print quota entry from the database.""" 1541 1541 self.doDelete(gpquota.ident) 1542 1543 def deletePrinter(self, printer) : 1542 1543 def deletePrinter(self, printer) : 1544 1544 """Completely deletes a printer from the Quota Storage.""" 1545 1545 pname = unicodeToDatabase(printer.Name) … … 1564 1564 for (ident, fields) in result : 1565 1565 self.doDelete(ident) 1566 for parent in self.getParentPrinters(printer) : 1566 for parent in self.getParentPrinters(printer) : 1567 1567 try : 1568 1568 parent.uniqueMember.remove(printer.ident) 1569 except ValueError : 1569 except ValueError : 1570 1570 pass 1571 else : 1571 else : 1572 1572 fields = { 1573 1573 "uniqueMember" : parent.uniqueMember, 1574 } 1575 self.doModify(parent.ident, fields) 1576 self.doDelete(printer.ident) 1577 1574 } 1575 self.doModify(parent.ident, fields) 1576 self.doDelete(printer.ident) 1577 1578 1578 def deleteBillingCode(self, code) : 1579 1579 """Deletes a billing code from the Quota Storage (no entries are deleted from the history)""" 1580 1580 self.doDelete(code.ident) 1581 1582 def sortRecords(self, fields, records, default, ordering) : 1581 1582 def sortRecords(self, fields, records, default, ordering) : 1583 1583 """Sort records based on list of fields prefixed with '+' (ASC) or '-' (DESC).""" 1584 1584 fieldindexes = {} 1585 1585 for i in range(len(fields)) : 1586 1586 fieldindexes[fields[i]] = i 1587 if not ordering : 1587 if not ordering : 1588 1588 ordering = default 1589 orderby = [] 1589 orderby = [] 1590 1590 for orderkey in ordering : 1591 1591 # Create ordering hints, ignoring unknown fields … … 1598 1598 if index is not None : 1599 1599 orderby.append((+1, index)) 1600 else : 1600 else : 1601 1601 index = fieldindexes.get(orderkey) 1602 1602 if index is not None : 1603 1603 orderby.append((+1, index)) 1604 1605 def compare(x, y, orderby=orderby) : 1604 1605 def compare(x, y, orderby=orderby) : 1606 1606 """Compares two records.""" 1607 1607 i = 0 … … 1612 1612 if not result : 1613 1613 i += 1 1614 else : 1614 else : 1615 1615 return sign * result 1616 return 0 # identical keys 1617 1616 return 0 # identical keys 1617 1618 1618 records.sort(compare) 1619 1619 return records 1620 1620 1621 1621 def extractPrinters(self, extractonly={}, ordering=[]) : 1622 1622 """Extracts all printer records.""" … … 1629 1629 if entry.PassThrough in (1, "1", "t", "true", "T", "TRUE", "True") : 1630 1630 passthrough = "t" 1631 else : 1631 else : 1632 1632 passthrough = "f" 1633 1633 result.append((entry.ident, entry.Name, entry.PricePerPage, entry.PricePerJob, entry.Description, entry.MaxJobSize, passthrough)) 1634 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1635 1634 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1635 1636 1636 def extractUsers(self, extractonly={}, ordering=[]) : 1637 1637 """Extracts all user records.""" … … 1644 1644 result.append((entry.ident, entry.Name, entry.AccountBalance, entry.LifeTimePaid, entry.LimitBy, entry.Email, entry.Description, entry.OverCharge)) 1645 1645 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1646 1646 1647 1647 def extractBillingcodes(self, extractonly={}, ordering=[]) : 1648 1648 """Extracts all billing codes records.""" … … 1655 1655 result.append((entry.ident, entry.BillingCode, entry.Balance, entry.PageCounter, entry.Description)) 1656 1656 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1657 1657 1658 1658 def extractGroups(self, extractonly={}, ordering=[]) : 1659 1659 """Extracts all group records.""" … … 1666 1666 result.append((entry.ident, entry.Name, entry.LimitBy, entry.AccountBalance, entry.LifeTimePaid, entry.Description)) 1667 1667 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1668 1668 1669 1669 def extractPayments(self, extractonly={}, ordering=[]) : 1670 1670 """Extracts all payment records.""" … … 1685 1685 result.append((entry.Name, amount, date, description)) 1686 1686 return [fields] + self.sortRecords(fields, result, ["+date"], ordering) 1687 1687 1688 1688 def extractUpquotas(self, extractonly={}, ordering=[]) : 1689 1689 """Extracts all userpquota records.""" … … 1698 1698 result.append((user.Name, entry.Name, userpquota.ident, user.ident, entry.ident, userpquota.LifePageCounter, userpquota.PageCounter, userpquota.SoftLimit, userpquota.HardLimit, userpquota.DateLimit)) 1699 1699 return [fields] + self.sortRecords(fields, result, ["+userdn"], ordering) 1700 1700 1701 1701 def extractGpquotas(self, extractonly={}, ordering=[]) : 1702 1702 """Extracts all grouppquota records.""" … … 1711 1711 result.append((group.Name, entry.Name, grouppquota.ident, group.ident, entry.ident, grouppquota.LifePageCounter, grouppquota.PageCounter, grouppquota.SoftLimit, grouppquota.HardLimit, grouppquota.DateLimit)) 1712 1712 return [fields] + self.sortRecords(fields, result, ["+groupdn"], ordering) 1713 1713 1714 1714 def extractUmembers(self, extractonly={}, ordering=[]) : 1715 1715 """Extracts all user groups members.""" … … 1725 1725 result.append((entry.Name, member.Name, entry.ident, member.ident)) 1726 1726 return [fields] + self.sortRecords(fields, result, ["+groupdn", "+userdn"], ordering) 1727 1727 1728 1728 def extractPmembers(self, extractonly={}, ordering=[]) : 1729 1729 """Extracts all printer groups members.""" … … 1739 1739 result.append((parent.Name, entry.Name, parent.ident, entry.ident)) 1740 1740 return [fields] + self.sortRecords(fields, result, ["+pgroupdn", "+printerdn"], ordering) 1741 1741 1742 1742 def extractHistory(self, extractonly={}, ordering=[]) : 1743 1743 """Extracts all jobhistory records.""" … … 1745 1745 if uname : 1746 1746 user = self.getUser(uname) 1747 else : 1747 else : 1748 1748 user = None 1749 1749 pname = extractonly.get("printername") 1750 1750 if pname : 1751 1751 printer = self.getPrinter(pname) 1752 else : 1752 else : 1753 1753 printer = None 1754 1754 startdate = extractonly.get("start") … … 1760 1760 result = [] 1761 1761 for entry in entries : 1762 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)) 1762 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)) 1763 1763 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1764 1764 1765 1765 def getBillingCodeFromBackend(self, label) : 1766 1766 """Extracts billing code information given its label : returns first matching billing code.""" … … 1777 1777 code.PageCounter = int(fields.get("pykotaPageCounter", [0])[0]) 1778 1778 code.Balance = float(fields.get("pykotaBalance", [0.0])[0]) 1779 code.Description = databaseToUnicode(fields.get("description", [""])[0]) 1779 code.Description = databaseToUnicode(fields.get("description", [""])[0]) 1780 1780 code.Exists = True 1781 return code 1782 1781 return code 1782 1783 1783 def addBillingCode(self, bcode) : 1784 1784 """Adds a billing code to the quota storage, returns it.""" … … 1793 1793 "pykotaPageCounter" : str(bcode.PageCounter or 0), 1794 1794 "pykotaBalance" : str(bcode.Balance or 0.0), 1795 "description" : unicodeToDatabase(bcode.Description or ""), 1796 } 1795 "description" : unicodeToDatabase(bcode.Description or ""), 1796 } 1797 1797 self.doAdd(dn, fields) 1798 1798 bcode.isDirty = False 1799 1799 return None # the entry created doesn't need further modification 1800 1800 1801 1801 def saveBillingCode(self, bcode) : 1802 1802 """Sets the new description for a billing code.""" 1803 1803 fields = { 1804 "description" : unicodeToDatabase(bcode.Description or ""), 1804 "description" : unicodeToDatabase(bcode.Description or ""), 1805 1805 "pykotaPageCounter" : str(bcode.PageCounter or 0), 1806 1806 "pykotaBalance" : str(bcode.Balance or 0.0), 1807 1807 } 1808 1808 self.doModify(bcode.ident, fields) 1809 1809 1810 1810 def getMatchingBillingCodes(self, billingcodepattern) : 1811 1811 """Returns the list of all billing codes which match a certain pattern.""" … … 1818 1818 try : 1819 1819 patdict = {}.fromkeys(patterns) 1820 except AttributeError : 1820 except AttributeError : 1821 1821 # Python v2.2 or earlier 1822 1822 patdict = {} … … 1830 1830 code.PageCounter = int(fields.get("pykotaPageCounter", [0])[0]) 1831 1831 code.Balance = float(fields.get("pykotaBalance", [0.0])[0]) 1832 code.Description = databaseToUnicode(fields.get("description", [""])[0]) 1832 code.Description = databaseToUnicode(fields.get("description", [""])[0]) 1833 1833 code.Exists = True 1834 1834 codes.append(code) 1835 1835 self.cacheEntry("BILLINGCODES", code.BillingCode, code) 1836 return codes 1837 1836 return codes 1837 1838 1838 def consumeBillingCode(self, bcode, pagecounter, balance) : 1839 1839 """Consumes from a billing code.""" … … 1842 1842 "pykotaPageCounter" : { "operator" : "+", "value" : pagecounter, "convert" : int }, 1843 1843 } 1844 return self.doModify(bcode.ident, fields) 1845 1846 def refundJob(self, jobident) : 1844 return self.doModify(bcode.ident, fields) 1845 1846 def refundJob(self, jobident) : 1847 1847 """Marks a job as refunded in the history.""" 1848 1848 fields = { 1849 1849 "pykotaAction" : "REFUND", 1850 } 1851 self.doModify(jobident, fields) 1852 1850 } 1851 self.doModify(jobident, fields) 1852 1853 1853 def storageUserFromRecord(self, username, record) : 1854 1854 """Returns a StorageUser instance from a database record.""" … … 1856 1856 user.Exists = True 1857 1857 return user 1858 1858 1859 1859 def storageGroupFromRecord(self, groupname, record) : 1860 1860 """Returns a StorageGroup instance from a database record.""" … … 1862 1862 group.Exists = True 1863 1863 return group 1864 1864 1865 1865 def storagePrinterFromRecord(self, printername, record) : 1866 1866 """Returns a StoragePrinter instance from a database record.""" … … 1868 1868 printer.Exists = True 1869 1869 return printer 1870 1871 def setJobAttributesFromRecord(self, job, record) : 1870 1871 def setJobAttributesFromRecord(self, job, record) : 1872 1872 """Sets the attributes of a job from a database record.""" 1873 1873 job.Exists = True 1874 1874 1875 1875 def storageJobFromRecord(self, record) : 1876 1876 """Returns a StorageJob instance from a database record.""" … … 1878 1878 self.setJobAttributesFromRecord(job, record) 1879 1879 return job 1880 1880 1881 1881 def storageLastJobFromRecord(self, printer, record) : 1882 1882 """Returns a StorageLastJob instance from a database record.""" … … 1884 1884 self.setJobAttributesFromRecord(lastjob, record) 1885 1885 return lastjob 1886 1886 1887 1887 def storageUserPQuotaFromRecord(self, user, printer, record) : 1888 1888 """Returns a StorageUserPQuota instance from a database record.""" … … 1890 1890 userpquota.Exists = True 1891 1891 return userpquota 1892 1892 1893 1893 def storageGroupPQuotaFromRecord(self, group, printer, record) : 1894 1894 """Returns a StorageGroupPQuota instance from a database record.""" … … 1896 1896 grouppquota.Exists = True 1897 1897 return grouppquota 1898 1898 1899 1899 def storageBillingCodeFromRecord(self, billingcode, record) : 1900 1900 """Returns a StorageBillingCode instance from a database record."""