root / pykota / trunk / pykota / tool.py @ 1196

Revision 1196, 28.3 kB (checked in by jalet, 20 years ago)

Code refactoring work.
Explicit redirection to /dev/null has to be set in external policy now, just
like in external mailto.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1# PyKota
2# -*- coding: ISO-8859-15 -*-
3
4# PyKota - Print Quotas for CUPS and LPRng
5#
6# (c) 2003 Jerome Alet <alet@librelogiciel.com>
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20#
21# $Id$
22#
23# $Log$
24# Revision 1.57  2003/11/19 23:19:38  jalet
25# Code refactoring work.
26# Explicit redirection to /dev/null has to be set in external policy now, just
27# like in external mailto.
28#
29# Revision 1.56  2003/11/19 07:40:20  jalet
30# Missing import statement.
31# Better documentation for mailto: external(...)
32#
33# Revision 1.55  2003/11/18 23:43:12  jalet
34# Mailto can be any external command now, as usual.
35#
36# Revision 1.54  2003/10/24 21:52:46  jalet
37# Now can force language when coming from CGI script.
38#
39# Revision 1.53  2003/10/08 21:41:38  jalet
40# External policies for printers works !
41# We can now auto-add users on first print, and do other useful things if needed.
42#
43# Revision 1.52  2003/10/07 09:07:28  jalet
44# Character encoding added to please latest version of Python
45#
46# Revision 1.51  2003/10/06 14:21:41  jalet
47# Test reversed to not retrieve group members when no messages for them.
48#
49# Revision 1.50  2003/10/02 20:23:18  jalet
50# Storage caching mechanism added.
51#
52# Revision 1.49  2003/07/29 20:55:17  jalet
53# 1.14 is out !
54#
55# Revision 1.48  2003/07/21 23:01:56  jalet
56# Modified some messages aout soft limit
57#
58# Revision 1.47  2003/07/16 21:53:08  jalet
59# Really big modifications wrt new configuration file's location and content.
60#
61# Revision 1.46  2003/07/09 20:17:07  jalet
62# Email field added to PostgreSQL schema
63#
64# Revision 1.45  2003/07/08 19:43:51  jalet
65# Configurable warning messages.
66# Poor man's treshold value added.
67#
68# Revision 1.44  2003/07/07 11:49:24  jalet
69# Lots of small fixes with the help of PyChecker
70#
71# Revision 1.43  2003/07/04 09:06:32  jalet
72# Small bug fix wrt undefined "LimitBy" field.
73#
74# Revision 1.42  2003/06/30 12:46:15  jalet
75# Extracted reporting code.
76#
77# Revision 1.41  2003/06/25 14:10:01  jalet
78# Hey, it may work (edpykota --reset excepted) !
79#
80# Revision 1.40  2003/06/10 16:37:54  jalet
81# Deletion of the second user which is not needed anymore.
82# Added a debug configuration field in /etc/pykota.conf
83# All queries can now be sent to the logger in debug mode, this will
84# greatly help improve performance when time for this will come.
85#
86# Revision 1.39  2003/04/29 18:37:54  jalet
87# Pluggable accounting methods (actually doesn't support external scripts)
88#
89# Revision 1.38  2003/04/24 11:53:48  jalet
90# Default policy for unknown users/groups is to DENY printing instead
91# of the previous default to ALLOW printing. This is to solve an accuracy
92# problem. If you set the policy to ALLOW, jobs printed by in nexistant user
93# (from PyKota's POV) will be charged to the next user who prints on the
94# same printer.
95#
96# Revision 1.37  2003/04/24 08:08:27  jalet
97# Debug message forgotten
98#
99# Revision 1.36  2003/04/24 07:59:40  jalet
100# LPRng support now works !
101#
102# Revision 1.35  2003/04/23 22:13:57  jalet
103# Preliminary support for LPRng added BUT STILL UNTESTED.
104#
105# Revision 1.34  2003/04/17 09:26:21  jalet
106# repykota now reports account balances too.
107#
108# Revision 1.33  2003/04/16 12:35:49  jalet
109# Groups quota work now !
110#
111# Revision 1.32  2003/04/16 08:53:14  jalet
112# Printing can now be limited either by user's account balance or by
113# page quota (the default). Quota report doesn't include account balance
114# yet, though.
115#
116# Revision 1.31  2003/04/15 11:30:57  jalet
117# More work done on money print charging.
118# Minor bugs corrected.
119# All tools now access to the storage as priviledged users, repykota excepted.
120#
121# Revision 1.30  2003/04/10 21:47:20  jalet
122# Job history added. Upgrade script neutralized for now !
123#
124# Revision 1.29  2003/03/29 13:45:27  jalet
125# GPL paragraphs were incorrectly (from memory) copied into the sources.
126# Two README files were added.
127# Upgrade script for PostgreSQL pre 1.01 schema was added.
128#
129# Revision 1.28  2003/03/29 13:08:28  jalet
130# Configuration is now expected to be found in /etc/pykota.conf instead of
131# in /etc/cups/pykota.conf
132# Installation script can move old config files to the new location if needed.
133# Better error handling if configuration file is absent.
134#
135# Revision 1.27  2003/03/15 23:01:28  jalet
136# New mailto option in configuration file added.
137# No time to test this tonight (although it should work).
138#
139# Revision 1.26  2003/03/09 23:58:16  jalet
140# Comment
141#
142# Revision 1.25  2003/03/07 22:56:14  jalet
143# 0.99 is out with some bug fixes.
144#
145# Revision 1.24  2003/02/27 23:48:41  jalet
146# Correctly maps PyKota's log levels to syslog log levels
147#
148# Revision 1.23  2003/02/27 22:55:20  jalet
149# WARN log priority doesn't exist.
150#
151# Revision 1.22  2003/02/27 09:09:20  jalet
152# Added a method to match strings against wildcard patterns
153#
154# Revision 1.21  2003/02/17 23:01:56  jalet
155# Typos
156#
157# Revision 1.20  2003/02/17 22:55:01  jalet
158# More options can now be set per printer or globally :
159#
160#       admin
161#       adminmail
162#       gracedelay
163#       requester
164#
165# the printer option has priority when both are defined.
166#
167# Revision 1.19  2003/02/10 11:28:45  jalet
168# Localization
169#
170# Revision 1.18  2003/02/10 01:02:17  jalet
171# External requester is about to work, but I must sleep
172#
173# Revision 1.17  2003/02/09 13:05:43  jalet
174# Internationalization continues...
175#
176# Revision 1.16  2003/02/09 12:56:53  jalet
177# Internationalization begins...
178#
179# Revision 1.15  2003/02/08 22:09:52  jalet
180# Name check method moved here
181#
182# Revision 1.14  2003/02/07 10:42:45  jalet
183# Indentation problem
184#
185# Revision 1.13  2003/02/07 08:34:16  jalet
186# Test wrt date limit was wrong
187#
188# Revision 1.12  2003/02/06 23:20:02  jalet
189# warnpykota doesn't need any user/group name argument, mimicing the
190# warnquota disk quota tool.
191#
192# Revision 1.11  2003/02/06 22:54:33  jalet
193# warnpykota should be ok
194#
195# Revision 1.10  2003/02/06 15:03:11  jalet
196# added a method to set the limit date
197#
198# Revision 1.9  2003/02/06 10:39:23  jalet
199# Preliminary edpykota work.
200#
201# Revision 1.8  2003/02/06 09:19:02  jalet
202# More robust behavior (hopefully) when the user or printer is not managed
203# correctly by the Quota System : e.g. cupsFilter added in ppd file, but
204# printer and/or user not 'yet?' in storage.
205#
206# Revision 1.7  2003/02/06 00:00:45  jalet
207# Now includes the printer name in email messages
208#
209# Revision 1.6  2003/02/05 23:55:02  jalet
210# Cleaner email messages
211#
212# Revision 1.5  2003/02/05 23:45:09  jalet
213# Better DateTime manipulation wrt grace delay
214#
215# Revision 1.4  2003/02/05 23:26:22  jalet
216# Incorrect handling of grace delay
217#
218# Revision 1.3  2003/02/05 22:16:20  jalet
219# DEVICE_URI is undefined outside of CUPS, i.e. for normal command line tools
220#
221# Revision 1.2  2003/02/05 22:10:29  jalet
222# Typos
223#
224# Revision 1.1  2003/02/05 21:28:17  jalet
225# Initial import into CVS
226#
227#
228#
229
230import sys
231import os
232import fnmatch
233import getopt
234import smtplib
235import gettext
236import locale
237
238from mx import DateTime
239
240from pykota import version, config, storage, logger
241from pykota.accounter import openAccounter
242
243class PyKotaToolError(Exception):
244    """An exception for PyKota config related stuff."""
245    def __init__(self, message = ""):
246        self.message = message
247        Exception.__init__(self, message)
248    def __repr__(self):
249        return self.message
250    __str__ = __repr__
251   
252class PyKotaTool :   
253    """Base class for all PyKota command line tools."""
254    def __init__(self, lang=None, doc="PyKota %s (c) 2003 %s" % (version.__version__, version.__author__)) :
255        """Initializes the command line tool."""
256        # locale stuff
257        try :
258            locale.setlocale(locale.LC_ALL, lang)
259            gettext.install("pykota")
260        except (locale.Error, IOError) :
261            gettext.NullTranslations().install()
262            sys.stderr.write("PyKota : Error while loading translations\n")
263   
264        # pykota specific stuff
265        self.documentation = doc
266        self.config = config.PyKotaConfig("/etc/pykota")
267        self.logger = logger.openLogger(self)
268        self.debug = self.config.getDebug()
269        self.storage = storage.openConnection(self)
270        self.smtpserver = self.config.getSMTPServer()
271       
272    def logdebug(self, message) :   
273        """Logs something to debug output if debug is enabled."""
274        if self.debug :
275            self.logger.log_message(message, "debug")
276       
277    def clean(self) :   
278        """Ensures that the database is closed."""
279        try :
280            self.storage.close()
281        except (TypeError, NameError, AttributeError) :   
282            pass
283           
284    def display_version_and_quit(self) :
285        """Displays version number, then exists successfully."""
286        self.clean()
287        print version.__version__
288        sys.exit(0)
289   
290    def display_usage_and_quit(self) :
291        """Displays command line usage, then exists successfully."""
292        self.clean()
293        print self.documentation
294        sys.exit(0)
295       
296    def parseCommandline(self, argv, short, long, allownothing=0) :
297        """Parses the command line, controlling options."""
298        # split options in two lists: those which need an argument, those which don't need any
299        withoutarg = []
300        witharg = []
301        lgs = len(short)
302        i = 0
303        while i < lgs :
304            ii = i + 1
305            if (ii < lgs) and (short[ii] == ':') :
306                # needs an argument
307                witharg.append(short[i])
308                ii = ii + 1 # skip the ':'
309            else :
310                # doesn't need an argument
311                withoutarg.append(short[i])
312            i = ii
313               
314        for option in long :
315            if option[-1] == '=' :
316                # needs an argument
317                witharg.append(option[:-1])
318            else :
319                # doesn't need an argument
320                withoutarg.append(option)
321       
322        # we begin with all possible options unset
323        parsed = {}
324        for option in withoutarg + witharg :
325            parsed[option] = None
326       
327        # then we parse the command line
328        args = []       # to not break if something unexpected happened
329        try :
330            options, args = getopt.getopt(argv, short, long)
331            if options :
332                for (o, v) in options :
333                    # we skip the '-' chars
334                    lgo = len(o)
335                    i = 0
336                    while (i < lgo) and (o[i] == '-') :
337                        i = i + 1
338                    o = o[i:]
339                    if o in witharg :
340                        # needs an argument : set it
341                        parsed[o] = v
342                    elif o in withoutarg :
343                        # doesn't need an argument : boolean
344                        parsed[o] = 1
345                    else :
346                        # should never occur
347                        raise PyKotaToolError, "Unexpected problem when parsing command line"
348            elif (not args) and (not allownothing) and sys.stdin.isatty() : # no option and no argument, we display help if we are a tty
349                self.display_usage_and_quit()
350        except getopt.error, msg :
351            sys.stderr.write("%s\n" % msg)
352            sys.stderr.flush()
353            self.display_usage_and_quit()
354        return (parsed, args)
355   
356    def isValidName(self, name) :
357        """Checks if a user or printer name is valid."""
358        # unfortunately Python 2.1 string modules doesn't define ascii_letters...
359        asciiletters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
360        digits = '0123456789'
361        if name[0] in asciiletters :
362            validchars = asciiletters + digits + "-_"
363            for c in name[1:] :
364                if c not in validchars :
365                    return 0
366            return 1       
367        return 0
368       
369    def matchString(self, s, patterns) :
370        """Returns 1 if the string s matches one of the patterns, else 0."""
371        for pattern in patterns :
372            if fnmatch.fnmatchcase(s, pattern) :
373                return 1
374        return 0
375       
376    def sendMessage(self, adminmail, touser, fullmessage) :
377        """Sends an email message containing headers to some user."""
378        if "@" not in touser :
379            touser = "%s@%s" % (touser, self.smtpserver)
380        server = smtplib.SMTP(self.smtpserver)
381        try :
382            server.sendmail(adminmail, [touser], fullmessage)
383        except smtplib.SMTPRecipientsRefused, answer :   
384            for (k, v) in answer.recipients.items() :
385                self.logger.log_message(_("Impossible to send mail to %s, error %s : %s") % (k, v[0], v[1]), "error")
386        server.quit()
387       
388    def sendMessageToUser(self, admin, adminmail, user, subject, message) :
389        """Sends an email message to a user."""
390        message += _("\n\nPlease contact your system administrator :\n\n\t%s - <%s>\n") % (admin, adminmail)
391        self.sendMessage(adminmail, user.Email or user.Name, "Subject: %s\n\n%s" % (subject, message))
392       
393    def sendMessageToAdmin(self, adminmail, subject, message) :
394        """Sends an email message to the Print Quota administrator."""
395        self.sendMessage(adminmail, adminmail, "Subject: %s\n\n%s" % (subject, message))
396       
397    def checkGroupPQuota(self, grouppquota) :   
398        """Checks the group quota on a printer and deny or accept the job."""
399        group = grouppquota.Group
400        printer = grouppquota.Printer
401        if group.LimitBy and (group.LimitBy.lower() == "balance") : 
402            if group.AccountBalance <= 0.0 :
403                action = "DENY"
404            elif group.AccountBalance <= self.config.getPoorMan() :   
405                action = "WARN"
406            else :   
407                action = "ALLOW"
408        else :
409            if grouppquota.SoftLimit is not None :
410                softlimit = int(grouppquota.SoftLimit)
411                if grouppquota.PageCounter < softlimit :
412                    action = "ALLOW"
413                else :   
414                    if grouppquota.HardLimit is None :
415                        # only a soft limit, this is equivalent to having only a hard limit
416                        action = "DENY"
417                    else :   
418                        hardlimit = int(grouppquota.HardLimit)
419                        if softlimit <= grouppquota.PageCounter < hardlimit :   
420                            now = DateTime.now()
421                            if grouppquota.DateLimit is not None :
422                                datelimit = DateTime.ISO.ParseDateTime(grouppquota.DateLimit)
423                            else :
424                                datelimit = now + self.config.getGraceDelay(printer.Name)
425                                grouppquota.setDateLimit(datelimit)
426                            if now < datelimit :
427                                action = "WARN"
428                            else :   
429                                action = "DENY"
430                        else :         
431                            action = "DENY"
432            else :       
433                if grouppquota.HardLimit is not None :
434                    # no soft limit, only a hard one.
435                    hardlimit = int(grouppquota.HardLimit)
436                    if grouppquota.PageCounter < hardlimit :
437                        action = "ALLOW"
438                    else :     
439                        action = "DENY"
440                else :
441                    # Both are unset, no quota, i.e. accounting only
442                    action = "ALLOW"
443        return action
444   
445    def checkUserPQuota(self, userpquota) :
446        """Checks the user quota on a printer and deny or accept the job."""
447        user = userpquota.User
448        printer = userpquota.Printer
449       
450        # first we check any group the user is a member of
451        for group in self.storage.getUserGroups(user) :
452            grouppquota = self.storage.getGroupPQuota(group, printer)
453            if grouppquota.Exists :
454                action = self.checkGroupPQuota(grouppquota)
455                if action == "DENY" :
456                    return action
457               
458        # then we check the user's own quota
459        # if we get there we are sure that policy is not EXTERNAL
460        (policy, dummy) = self.config.getPrinterPolicy(printer.Name)
461        if user.LimitBy and (user.LimitBy.lower() == "balance") : 
462            if user.AccountBalance is None :
463                if policy == "ALLOW" :
464                    action = "POLICY_ALLOW"
465                else :   
466                    action = "POLICY_DENY"
467                self.logger.log_message(_("Unable to find user %s's account balance, applying default policy (%s) for printer %s") % (user.Name, action, printer.Name))
468            else :   
469                val = float(user.AccountBalance or 0.0)
470                if val <= 0.0 :
471                    action = "DENY"
472                elif val <= self.config.getPoorMan() :   
473                    action = "WARN"
474                else :   
475                    action = "ALLOW"
476        else :
477            if not userpquota.Exists :
478                # Unknown userquota
479                if policy == "ALLOW" :
480                    action = "POLICY_ALLOW"
481                else :   
482                    action = "POLICY_DENY"
483                self.logger.log_message(_("Unable to match user %s on printer %s, applying default policy (%s)") % (user.Name, printer.Name, action))
484            else :   
485                pagecounter = int(userpquota.PageCounter or 0)
486                if userpquota.SoftLimit is not None :
487                    softlimit = int(userpquota.SoftLimit)
488                    if pagecounter < softlimit :
489                        action = "ALLOW"
490                    else :   
491                        if userpquota.HardLimit is None :
492                            # only a soft limit, this is equivalent to having only a hard limit
493                            action = "DENY"
494                        else :   
495                            hardlimit = int(userpquota.HardLimit)
496                            if softlimit <= pagecounter < hardlimit :   
497                                now = DateTime.now()
498                                if userpquota.DateLimit is not None :
499                                    datelimit = DateTime.ISO.ParseDateTime(userpquota.DateLimit)
500                                else :
501                                    datelimit = now + self.config.getGraceDelay(printer.Name)
502                                    userpquota.setDateLimit(datelimit)
503                                if now < datelimit :
504                                    action = "WARN"
505                                else :   
506                                    action = "DENY"
507                            else :         
508                                action = "DENY"
509                else :       
510                    if userpquota.HardLimit is not None :
511                        # no soft limit, only a hard one.
512                        hardlimit = int(userpquota.HardLimit)
513                        if pagecounter < hardlimit :
514                            action = "ALLOW"
515                        else :     
516                            action = "DENY"
517                    else :
518                        # Both are unset, no quota, i.e. accounting only
519                        action = "ALLOW"
520        return action
521   
522    def externalMailTo(self, cmd, action, user, printer, message) :
523        """Warns the user with an external command."""
524        username = user.Name
525        printername = printer.Name
526        email = user.Email or user.Name
527        if "@" not in email :
528            email = "%s@%s" % (email, self.smtpserver)
529        os.system(cmd % locals())
530   
531    def formatCommandLine(self, cmd, user, printer) :
532        """Executes an external command."""
533        username = user.Name
534        printername = printer.Name
535        return cmd % locals()
536       
537    def warnGroupPQuota(self, grouppquota) :
538        """Checks a group quota and send messages if quota is exceeded on current printer."""
539        group = grouppquota.Group
540        printer = grouppquota.Printer
541        admin = self.config.getAdmin(printer.Name)
542        adminmail = self.config.getAdminMail(printer.Name)
543        (mailto, arguments) = self.config.getMailTo(printer.Name)
544        action = self.checkGroupPQuota(grouppquota)
545        if action.startswith("POLICY_") :
546            action = action[7:]
547        if action == "DENY" :
548            adminmessage = _("Print Quota exceeded for group %s on printer %s") % (group.Name, printer.Name)
549            self.logger.log_message(adminmessage)
550            if mailto in [ "BOTH", "ADMIN" ] :
551                self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
552            if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
553                for user in self.storage.getGroupMembers(group) :
554                    if mailto != "EXTERNAL" :
555                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), self.config.getHardWarn(printer.Name))
556                    else :   
557                        self.externalMailTo(arguments, action, user, printer, message)
558        elif action == "WARN" :   
559            adminmessage = _("Print Quota low for group %s on printer %s") % (group.Name, printer.Name)
560            self.logger.log_message(adminmessage)
561            if mailto in [ "BOTH", "ADMIN" ] :
562                self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
563            if group.LimitBy and (group.LimitBy.lower() == "balance") : 
564                message = self.config.getPoorWarn()
565            else :     
566                message = self.config.getSoftWarn(printer.Name)
567            if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
568                for user in self.storage.getGroupMembers(group) :
569                    if mailto != "EXTERNAL" :
570                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
571                    else :   
572                        self.externalMailTo(arguments, action, user, printer, message)
573        return action       
574       
575    def warnUserPQuota(self, userpquota) :
576        """Checks a user quota and send him a message if quota is exceeded on current printer."""
577        user = userpquota.User
578        printer = userpquota.Printer
579        admin = self.config.getAdmin(printer.Name)
580        adminmail = self.config.getAdminMail(printer.Name)
581        (mailto, arguments) = self.config.getMailTo(printer.Name)
582        action = self.checkUserPQuota(userpquota)
583        if action.startswith("POLICY_") :
584            action = action[7:]
585        if action == "DENY" :
586            adminmessage = _("Print Quota exceeded for user %s on printer %s") % (user.Name, printer.Name)
587            self.logger.log_message(adminmessage)
588            if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
589                message = self.config.getHardWarn(printer.Name)
590                if mailto != "EXTERNAL" :
591                    self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
592                else :   
593                    self.externalMailTo(arguments, action, user, printer, message)
594            if mailto in [ "BOTH", "ADMIN" ] :
595                self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
596        elif action == "WARN" :   
597            adminmessage = _("Print Quota low for user %s on printer %s") % (user.Name, printer.Name)
598            self.logger.log_message(adminmessage)
599            if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
600                if user.LimitBy and (user.LimitBy.lower() == "balance") : 
601                    message = self.config.getPoorWarn()
602                else :     
603                    message = self.config.getSoftWarn(printer.Name)
604                if mailto != "EXTERNAL" :   
605                    self.sendMessageToUser(admin, adminmail, user, _("Print Quota Low"), message)
606                else :   
607                    self.externalMailTo(arguments, action, user, printer, message)
608            if mailto in [ "BOTH", "ADMIN" ] :
609                self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
610        return action       
611       
612class PyKotaFilterOrBackend(PyKotaTool) :   
613    """Class for the PyKota filter or backend."""
614    def __init__(self) :
615        PyKotaTool.__init__(self)
616        (self.printingsystem, \
617         self.printerhostname, \
618         self.printername, \
619         self.username, \
620         self.jobid, \
621         self.inputfile, \
622         self.copies, \
623         self.title, \
624         self.options, \
625         self.originalbackend) = self.extractInfoFromCupsOrLprng()
626        self.accounter = openAccounter(self)
627   
628    def extractInfoFromCupsOrLprng(self) :   
629        """Returns a tuple (printingsystem, printerhostname, printername, username, jobid, filename, title, options, backend).
630       
631           Returns (None, None, None, None, None, None, None, None, None, None) if no printing system is recognized.
632        """
633        # Try to detect CUPS
634        if os.environ.has_key("CUPS_SERVERROOT") and os.path.isdir(os.environ.get("CUPS_SERVERROOT", "")) :
635            if len(sys.argv) == 7 :
636                inputfile = sys.argv[6]
637            else :   
638                inputfile = None
639               
640            # check that the DEVICE_URI environment variable's value is
641            # prefixed with "cupspykota:" otherwise don't touch it.
642            # If this is the case, we have to remove the prefix from
643            # the environment before launching the real backend in cupspykota
644            device_uri = os.environ.get("DEVICE_URI", "")
645            if device_uri.startswith("cupspykota:") :
646                fulldevice_uri = device_uri[:]
647                device_uri = fulldevice_uri[len("cupspykota:"):]
648                if device_uri.startswith("//") :    # lpd (at least)
649                    device_uri = device_uri[2:]
650                os.environ["DEVICE_URI"] = device_uri   # TODO : side effect !
651            # TODO : check this for more complex urls than ipp://myprinter.dot.com:631/printers/lp
652            try :
653                (backend, destination) = device_uri.split(":", 1) 
654            except ValueError :   
655                raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri
656            while destination.startswith("/") :
657                destination = destination[1:]
658            printerhostname = destination.split("/")[0].split(":")[0]
659            return ("CUPS", \
660                    printerhostname, \
661                    os.environ.get("PRINTER"), \
662                    sys.argv[2].strip(), \
663                    sys.argv[1].strip(), \
664                    inputfile, \
665                    int(sys.argv[4].strip()), \
666                    sys.argv[3], \
667                    sys.argv[5], \
668                    backend)
669        else :   
670            # Try to detect LPRng
671            jseen = Pseen = nseen = rseen = Kseen = None
672            for arg in sys.argv :
673                if arg.startswith("-j") :
674                    jseen = arg[2:].strip()
675                elif arg.startswith("-n") :     
676                    nseen = arg[2:].strip()
677                elif arg.startswith("-P") :   
678                    Pseen = arg[2:].strip()
679                elif arg.startswith("-r") :   
680                    rseen = arg[2:].strip()
681                elif arg.startswith("-K") or arg.startswith("-#") :   
682                    Kseen = int(arg[2:].strip())
683            if Kseen is None :       
684                Kseen = 1       # we assume the user wants at least one copy...
685            if (rseen is None) and jseen and Pseen and nseen :   
686                self.logger.log_message(_("Printer hostname undefined, set to 'localhost'"), "warn")
687                rseen = "localhost"
688            if jseen and Pseen and nseen and rseen :       
689                # job is always in stdin (None)
690                return ("LPRNG", rseen, Pseen, nseen, jseen, None, Kseen, None, None, None)
691        self.logger.log_message(_("Printing system unknown, args=%s") % " ".join(sys.argv), "warn")
692        return (None, None, None, None, None, None, None, None, None, None)   # Unknown printing system
Note: See TracBrowser for help on using the browser.