#! /usr/bin/env python
# -*- coding: ISO-8859-15 -*-

# PyKota Print Quota Editor 
#
# PyKota - Print Quotas for CUPS and LPRng
#
# (c) 2003, 2004, 2005, 2006, 2007 Jerome Alet <alet@librelogiciel.com>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# $Id: edpykota 3133 2007-01-17 22:19:42Z jerome $
#
#

import sys

from pykota.tool import Percent, PyKotaTool, PyKotaCommandLineError, crashed, N_
from pykota.storage import StorageUserPQuota, StorageGroupPQuota

__doc__ = N_("""edpykota v%(__version__)s (c) %(__years__)s %(__author__)s

A Print Quota editor for PyKota.

command line usage :

  edpykota [options] user1 user2 ... userN
  
  edpykota [options] group1 group2 ... groupN

options :

  -v | --version       Prints edpykota's version number then exits.
  -h | --help          Prints this message then exits.
  
  -a | --add           Adds users or groups print quota entries if
                       they don't exist in database.
                       
  -d | --delete        Deletes users or groups print quota entries.
                       Users or groups are never deleted, you have
                       to use the pkusers command to delete them.
                       The history will be purge from all matching
                       jobs, unless -g | --groups is used.
  
  -P | --printer p     Edit quotas on printer p only. Actually p can
                       use wildcards characters to select only
                       some printers. The default value is *, meaning
                       all printers. 
                       You can specify several names or wildcards, 
                       by separating them with commas.
  
  -g | --groups        Edit groups print quota entries instead of 
                       users print quota entries.
                          
  -L | --list          Lists users or groups print quota entries.
  
  -n | --noquota       Sets both soft and hard limits to None for users
                       or groups print quota entries.
  
  -r | --reset         Resets the actual page counter for the user
                       or group to zero on the specified printers. 
                       The life time page counter is kept unchanged.
                       
  -R | --hardreset     Resets the actual and life time page counters
                       for the user or group to zero on the specified 
                       printers. This is a shortcut for '--used 0'.
                       
  -s | --skipexisting  In combination with the --add option above, tells
                       edpykota to not modify existing print quota entries.
                       
  -S | --softlimit sl  Sets the quota soft limit to sl pages.                       
  
  -H | --hardlimit hl  Sets the quota hard limit to hl pages.
  
  -I | --increase v    Increase existing Soft and Hard limits by the value
                       of v. You can prefix v with + or -, if no sign is
                       used, + is assumed.

  -U | --used u        Sets the page counters for the user u pages on
                       the selected printers. Doesn't work for groups, since
                       their page counters are the sum of all their members'
                       page counters.
                       Useful for migrating users from a different system
                       where they have already used some pages. Actual
                       and Life Time page counters may be increased or decreased
                       if u is prefixed with + or -.
                       WARNING : BOTH page counters are modified in all cases,
                       so be careful.
                       NB : if u equals '0', then the action taken is
                       the same as if --hardreset was used.

  user1 through userN and group1 through groupN can use wildcards
  if the --add option is not set.
  
examples :                              

  $ edpykota --add john paul george ringo
  
  This will create print quota entries for users john, paul, george
  and ringo on all printers. These print quota entries will have no
  limit set.
  
  $ edpykota --printer lp -S 50 -H 60 jerome
  
  This will set jerome's print quota on the lp printer to a soft limit
  of 50 pages, and a hard limit of 60 pages. Both user jerome and
  printer lp have been previously created with the pkusers and pkprinters
  commands, respectively.

  $ edpykota -g -S 500 -H 550 financial support            
  
  This will set print quota soft limit to 500 pages and hard limit
  to 550 pages for groups financial and support on all printers.
  
  $ edpykota --reset jerome "jo*"
  
  This will reset jerome's page counter to zero on all printers, as
  well as every user whose name begins with 'jo'.
  Their life time page counter on each printer will be kept unchanged.
  You can also reset the life time page counters by using the
  --hardreset | -R command line option.
  
  $ edpykota --printer hpcolor --noquota jerome
  
  This will tell PyKota to not limit jerome when printing on the 
  hpcolor printer. All his jobs will be allowed on this printer, but 
  accounting of the pages he prints will still be kept.
  Print Quotas for jerome on other printers are unchanged.
  
  $ edpykota --delete --printer "HP*,XER*" jerome rachel
  
  This will delete users jerome and rachel's print quota
  entries on all printers which name begin with 'HP' or
  'XER'. The jobs printed by these users on these printers
  will be deleted from the history.
""") 
        
class EdPyKota(PyKotaTool) :        
    """A class for edpykota."""
    def modifyPQEntry(self, pqkey, pqentry, noquota, softlimit, hardlimit, increase, reset, hardreset, suffix, used) :
        """Modifies a print quota entry."""
        if noquota or ((softlimit is not None) and (hardlimit is not None)) :
            pqentry.setLimits(softlimit, hardlimit)
        if increase :
            newsoft = (pqentry.SoftLimit or 0) + increase         
            newhard = (pqentry.HardLimit or 0) + increase         
            if (newsoft >= 0) and (newhard >= 0) :
                pqentry.setLimits(newsoft, newhard)
            else :    
                self.printInfo(_("You can't set negative limits for %s") % pqkey, "error")
        if reset :
            pqentry.reset()
        if hardreset :    
            pqentry.hardreset()
        if suffix == "User" :
            if used :
                pqentry.setUsage(used)
    
    def main(self, names, options) :
        """Edit user or group quotas."""
        names = self.sanitizeNames(options, names)
        suffix = (options["groups"] and "Group") or "User"        
        printernames = options["printer"].split(",")
            
        if not options["list"] :
            percent = Percent(self)
            percent.display("%s..." % _("Extracting datas"))
        printers = self.storage.getMatchingPrinters(options["printer"])
        entries = getattr(self.storage, "getMatching%ss" % suffix)(",".join(names))
        if not options["list"] :
            percent.setSize(len(printers) * len(entries))
        
        if options["list"] :
            for printer in printers :
                for entry in entries :
                    pqentry = getattr(self.storage, "get%sPQuota" % suffix)(entry, printer)
                    if pqentry.Exists :
                        print "%s@%s" % (entry.Name, printer.Name)
                        print "    %s" % (_("Page counter : %s") % pqentry.PageCounter)
                        print "    %s" % (_("Lifetime page counter : %s") % pqentry.LifePageCounter)
                        print "    %s" % (_("Soft limit : %s") % pqentry.SoftLimit)
                        print "    %s" % (_("Hard limit : %s") % pqentry.HardLimit)
                        print "    %s" % (_("Date limit : %s") % pqentry.DateLimit)
                        print "    %s (Not supported yet)" % (_("Maximum job size : %s") % ((pqentry.MaxJobSize and (_("%s pages") % pqentry.MaxJobSize)) or _("Unlimited")))
                        if hasattr(pqentry, "WarnCount") :
                            print "    %s" % (_("Warning banners printed : %s") % pqentry.WarnCount)
                        print
        elif options["delete"] :    
            percent.display("\n%s..." % _("Deletion"))
            getattr(self.storage, "deleteMany%sPQuotas" % suffix)(printers, entries)
            percent.display("\n")
        else :
            skipexisting = options["skipexisting"]
            used = options["used"]
            if used :
                used = used.strip()
                try :
                    int(used)
                except ValueError :
                    raise PyKotaCommandLineError, _("Invalid used value %s.") % used
                    
            increase = options["increase"]
            if increase :
                try :
                    increase = int(increase.strip())
                except ValueError :
                    raise PyKotaCommandLineError, _("Invalid increase value %s.") % increase
            
            noquota = options["noquota"]
            reset = options["reset"]        
            hardreset = options["hardreset"]
            softlimit = hardlimit = None
            if not noquota :
                if options["softlimit"] :
                    try :
                        softlimit = int(options["softlimit"].strip())
                        if softlimit < 0 :
                            raise ValueError
                    except ValueError :    
                        raise PyKotaCommandLineError, _("Invalid softlimit value %s.") % options["softlimit"]
                if options["hardlimit"] :
                    try :
                        hardlimit = int(options["hardlimit"].strip())
                        if hardlimit < 0 :
                            raise ValueError
                    except ValueError :    
                        raise PyKotaCommandLineError, _("Invalid hardlimit value %s.") % options["hardlimit"]
                if (softlimit is not None) and (hardlimit is not None) and (hardlimit < softlimit) :        
                    # error, exchange them
                    self.printInfo(_("Hard limit %i is less than soft limit %i, values will be exchanged.") % (hardlimit, softlimit))
                    (softlimit, hardlimit) = (hardlimit, softlimit)
                if hardlimit is None :    
                    hardlimit = softlimit
                    if hardlimit is not None :
                        self.printInfo(_("Undefined hard limit set to soft limit (%s).") % str(hardlimit))
                if softlimit is None :    
                    softlimit = hardlimit
                    if softlimit is not None :
                        self.printInfo(_("Undefined soft limit set to hard limit (%s).") % str(softlimit))
                        
            self.storage.beginTransaction()            
            try :
                if options["add"] :
                    percent.display("\n%s...\n" % _("Creation"))
                    if not entries :    
                        self.printInfo(_("No entry matches %s. Please use pkusers to create them first.") % (" ".join(names)), "warn")
                            
                    factory = globals()["Storage%sPQuota" % suffix]
                    for printer in printers :
                        pname = printer.Name
                        for entry in entries :
                            ename = entry.Name
                            pqkey = "%s@%s" % (ename, pname)
                            pqentry = factory(self.storage, entry, printer)
                            self.modifyPQEntry(pqkey, pqentry, noquota, \
                                                        softlimit, hardlimit, \
                                                        increase, reset, \
                                                        hardreset, suffix, used)
                            oldpqentry = getattr(self.storage, "add%sPQuota" % suffix)(pqentry)
                            if oldpqentry is not None :    
                                if skipexisting :
                                    self.logdebug("%s print quota entry %s@%s already exists, skipping." % (suffix, ename, pname))
                                else :    
                                    self.logdebug("%s print quota entry %s@%s already exists, will be modified." % (suffix, ename, pname))
                                    self.modifyPQEntry(pqkey, oldpqentry, noquota, \
                                                        softlimit, hardlimit, \
                                                        increase, reset, \
                                                        hardreset, suffix, used)
                                    oldpqentry.save()                    
                            percent.oneMore()
                else :        
                    percent.display("\n%s...\n" % _("Modification"))
                    for printer in printers :
                        for entry in entries :
                            pqkey = "%s@%s" % (entry.Name, printer.Name)
                            pqentry = getattr(self.storage, "get%sPQuota" % suffix)(entry, printer)
                            if pqentry.Exists :     
                                self.modifyPQEntry(pqkey, pqentry, noquota, \
                                                    softlimit, hardlimit, \
                                                    increase, reset, \
                                                    hardreset, suffix, used)
                                pqentry.save()        
                            percent.oneMore()
            except :                    
                self.storage.rollbackTransaction()
                raise
            else :    
                self.storage.commitTransaction()
                            
        if not options["list"] :
            percent.done()
            
if __name__ == "__main__" : 
    retcode = 0
    try :
        defaults = { \
                     "printer" : "*", \
                   }
        short_options = "vhdnagrLP:S:H:G:RU:I:s"
        long_options = ["help", "version", \
                        "delete", "list", \
                        "noquota", "add", \
                        "groups", "reset", "hardreset", \
                        "printer=", "softlimit=", "hardlimit=", \
                        "increase=", "used=", "skipexisting"]
        
        # Initializes the command line tool
        manager = EdPyKota(doc=__doc__)
        manager.deferredInit()
        
        # parse and checks the command line
        (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options)
        
        # sets long options
        options["help"] = options["h"] or options["help"]
        options["version"] = options["v"] or options["version"]
        options["add"] = options["a"] or options["add"]
        options["groups"] = options["g"] or options["groups"]
        options["printer"] = options["P"] or options["printer"] or defaults["printer"]
        options["softlimit"] = options["S"] or options["softlimit"]
        options["hardlimit"] = options["H"] or options["hardlimit"] 
        options["reset"] = options["r"] or options["reset"] 
        options["noquota"] = options["n"] or options["noquota"]
        options["delete"] = options["d"] or options["delete"] 
        options["hardreset"] = options["R"] or options["hardreset"] 
        options["used"] = options["U"] or options["used"]
        options["increase"] = options["I"] or options["increase"]
        options["list"] = options["L"] or options["list"]
        options["skipexisting"] = options["s"] or options["skipexisting"]
        
        if options["help"] :
            manager.display_usage_and_quit()
        elif options["version"] :
            manager.display_version_and_quit()
        elif (options["add"] and options["delete"]) \
             or (options["noquota"] and (options["hardlimit"] or options["softlimit"])) \
             or (options["groups"] and options["used"]) \
             or (options["skipexisting"] and not options["add"]) :
            raise PyKotaCommandLineError, _("incompatible options, see help.")
        elif options["delete"] and not args :
            raise PyKotaCommandLineError, _("You have to pass user or group names on the command line")
        else :
            retcode = manager.main(args, options)
    except KeyboardInterrupt :        
        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
        retcode = -3
    except PyKotaCommandLineError, msg :     
        sys.stderr.write("%s : %s\n" % (sys.argv[0], msg))
        retcode = -2
    except SystemExit :        
        pass
    except :
        try :
            manager.crashed("edpykota failed")
        except :    
            crashed("edpykota failed")
        retcode = -1

    try :
        manager.storage.close()
    except (TypeError, NameError, AttributeError) :    
        pass
        
    sys.exit(retcode)    
