131 | | gotSigTerm = 0 |
132 | | def sigterm_handler(signum, frame) : |
133 | | """Sets a global variable whenever SIGTERM is received.""" |
134 | | # SIGTERM will be ignore most of the time, but during |
135 | | # the call to the real backend, we have to pass it through. |
136 | | global gotSigTerm |
137 | | gotSigTerm = 1 |
138 | | |
139 | | def main(thebackend) : |
140 | | """Do it, and do it right !""" |
141 | | # first deal with signals |
142 | | # CUPS backends ignore SIGPIPE and exit(1) on SIGTERM |
143 | | # Fortunately SIGPIPE is already ignored by Python |
144 | | # It's there just in case this changes in the future. |
145 | | # Here we have to handle SIGTERM correctly, and pass |
146 | | # it to the original backend if needed. |
147 | | global gotSigTerm |
148 | | gotSigTerm = 0 |
149 | | signal.signal(signal.SIGPIPE, signal.SIG_IGN) |
150 | | signal.signal(signal.SIGTERM, sigterm_handler) |
151 | | |
152 | | # |
153 | | # retrieve some informations on the current printer and user |
154 | | # from the Quota Storage. |
155 | | printer = thebackend.storage.getPrinter(thebackend.printername) |
156 | | if not printer.Exists : |
157 | | # The printer is unknown from the Quota Storage perspective |
158 | | # we send an error back to CUPS, which will stop |
159 | | # the print queue. |
160 | | thebackend.logger.log_message(_("Printer %s not registered in the PyKota system") % thebackend.printername, "error") |
161 | | return 1 |
162 | | else : |
163 | | for dummy in range(2) : |
164 | | user = thebackend.storage.getUser(thebackend.username) |
165 | | if user.Exists : |
166 | | break |
167 | | else : |
168 | | # The user is unknown from the Quota Storage perspective |
169 | | # Depending on the default policy for this printer, we |
170 | | # either let the job pass through or reject it, but we |
171 | | # log a message in any case. |
172 | | (policy, args) = thebackend.config.getPrinterPolicy(thebackend.printername) |
173 | | if policy == "ALLOW" : |
174 | | action = "POLICY_ALLOW" |
175 | | elif policy == "EXTERNAL" : |
176 | | commandline = thebackend.formatCommandLine(args, user, printer) |
177 | | thebackend.logger.log_message(_("User %s not registered in the PyKota system, applying external policy (%s) for printer %s") % (thebackend.username, commandline, thebackend.printername), "info") |
178 | | if os.system(commandline) : |
179 | | # if an error occured, we die without error, |
180 | | # so that the job doesn't stop the print queue. |
181 | | thebackend.logger.log_message(_("External policy %s for printer %s produced an error. Job rejected. Please check PyKota's configuration files.") % (commandline, thebackend.printername), "error") |
182 | | return 0 |
183 | | else : |
184 | | # here we try a second time, because the goal |
185 | | # of the external action was to add the user |
186 | | # in the database. |
187 | | continue |
188 | | else : |
189 | | action = "POLICY_DENY" |
190 | | thebackend.logger.log_message(_("User %s not registered in the PyKota system, applying default policy (%s) for printer %s") % (thebackend.username, action, thebackend.printername), "warn") |
191 | | if action == "POLICY_DENY" : |
192 | | # if not allowed to print then die, else proceed. |
193 | | # we die without error, so that the job doesn't |
194 | | # stop the print queue. |
195 | | return 0 |
196 | | # when we get there, the printer policy allows the job to pass |
197 | | break |
| 143 | class PyKotaBackend(PyKotaFilterOrBackend) : |
| 144 | """A class for the pykota backend.""" |
| 145 | def acceptJob(self) : |
| 146 | """Returns the appropriate exit code to tell CUPS all is OK.""" |
| 147 | return 0 |
| 148 | |
| 149 | def removeJob(self) : |
| 150 | """Returns the appropriate exit code to let CUPS think all is OK. |
| 151 | |
| 152 | Returning 0 (success) prevents CUPS from stopping the print queue. |
| 153 | """ |
| 154 | return 0 |
| 155 | |
| 156 | def doWork(self, policy, printer, user, userpquota) : |
| 157 | """Most of the work is done here.""" |
| 158 | global gotSigTerm |
| 159 | # Two different values possible for policy here : |
| 160 | # ALLOW means : Either printer, user or user print quota doesn't exist, |
| 161 | # but the job should be allowed anyway. |
| 162 | # OK means : Both printer, user and user print quota exist, job should |
| 163 | # be allowed if current user is allowed to print on this printer |
| 164 | if policy == "OK" : |
| 165 | self.logdebug("Checking user %s's quota on printer %s" % (user.Name, printer.Name)) |
| 166 | action = self.warnUserPQuota(userpquota) |
| 167 | if action not in ["ALLOW", "WARN"] : |
| 168 | return self.removeJob() |
| 169 | self.logdebug("Job accounting begins.") |
| 170 | self.accounter.beginJob(userpquota) |
| 171 | |
| 172 | # pass the job's data to the real backend |
| 173 | if gotSigTerm : |
| 174 | retcode = self.removeJob() |
| 175 | else : |
| 176 | retcode = self.handleData() |
| 177 | |
| 178 | if policy == "OK" : |
| 179 | # stops accounting. |
| 180 | self.accounter.endJob(userpquota) |
| 181 | self.logdebug("Job accounting ends.") |
| 182 | |
| 183 | # retrieve the job size |
| 184 | jobsize = self.accounter.getJobSize() |
| 185 | self.logdebug("Job size : %i" % jobsize) |
| 186 | |
| 187 | # update the quota for the current user on this printer |
| 188 | jobprice = (float(printer.PricePerPage or 0.0) * jobsize) + float(printer.PricePerJob or 0.0) |
| 189 | if jobsize : |
| 190 | self.logdebug("Updating user %s's quota on printer %s" % (user.Name, printer.Name)) |
| 191 | userpquota.increasePagesUsage(jobsize) |
| 192 | |
| 193 | # adds the current job to history |
| 194 | printer.addJobToHistory(self.jobid, user, self.accounter.getLastPageCounter(), action, jobsize, jobprice, self.preserveinputfile, self.title, self.copies, self.options) |
| 195 | self.logdebug("Job added to history.") |
| 196 | |
| 197 | return retcode |
199 | | if user.Exists : |
200 | | # Is the current user allowed to print at all ? |
201 | | thebackend.logdebug("Checking user %s's quota on printer %s" % (user.Name, printer.Name)) |
202 | | action = thebackend.warnUserPQuota(thebackend.storage.getUserPQuota(user, printer)) |
203 | | elif policy == "EXTERNAL" : |
204 | | # if the extenal policy produced no error, but the |
205 | | # user still doesn't exist, we die without error, |
206 | | # so that the job doesn't stop the print queue. |
207 | | thebackend.logger.log_message(_("External policy %s for printer %s couldn't add user %s. Job rejected.") % (commandline, thebackend.printername, thebackend.username), "error") |
208 | | return 0 |
209 | | |
210 | | if action not in ["ALLOW", "WARN"] : |
211 | | # if not allowed to print then die, else proceed. |
212 | | # we die without error, so that the job doesn't |
213 | | # stop the print queue. |
214 | | retcode = 0 |
215 | | else : |
216 | | # pass the job untouched to the underlying layer |
217 | | # and starts accounting at the same time |
218 | | thebackend.logdebug("Job accounting begins.") |
219 | | thebackend.accounter.beginJob(printer, user) |
220 | | |
| 199 | def handleData(self) : |
| 200 | """Pass the job's data to the real backend.""" |
367 | | |
368 | | # stops accounting. |
369 | | thebackend.accounter.endJob(printer, user) |
370 | | thebackend.logdebug("Job accounting ends.") |
371 | | |
372 | | # retrieve the job size |
373 | | jobsize = thebackend.accounter.getJobSize() |
374 | | thebackend.logdebug("Job size : %i" % jobsize) |
375 | | |
376 | | # update the quota for the current user on this printer |
377 | | jobprice = (float(printer.PricePerPage or 0.0) * jobsize) + float(printer.PricePerJob or 0.0) |
378 | | if jobsize : |
379 | | userquota = thebackend.storage.getUserPQuota(user, printer) |
380 | | if userquota.Exists : |
381 | | thebackend.logdebug("Updating user %s's quota on printer %s" % (user.Name, printer.Name)) |
382 | | userquota.increasePagesUsage(jobsize) |
383 | | |
384 | | # adds the current job to history |
385 | | printer.addJobToHistory(thebackend.jobid, user, thebackend.accounter.getLastPageCounter(), action, jobsize, jobprice, thebackend.preserveinputfile, thebackend.title, thebackend.copies, thebackend.options) |
386 | | thebackend.logdebug("Job added to history.") |
387 | | |
388 | | return retcode # return (retcode or gotSigTerm) shouldn't be needed |
389 | | |
| 350 | return retcode |
| 351 | |