- Timestamp:
- 10/06/08 00:24:42 (16 years ago)
- Location:
- pkipplib/trunk
- Files:
-
- 11 modified
Legend:
- Unmodified
- Added
- Removed
-
pkipplib/trunk/bin/pksubscribe
r45 r3437 1 1 #! /usr/bin/env python 2 # -*- coding: UTF-8 -*-2 # -*- coding: utf-8 -*- 3 3 # 4 4 # pkipplib : IPP and CUPS support for Python … … 9 9 # the Free Software Foundation, either version 3 of the License, or 10 10 # (at your option) any later version. 11 # 11 # 12 12 # This program is distributed in the hope that it will be useful, 13 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 15 # GNU General Public License for more details. 16 # 16 # 17 17 # You should have received a copy of the GNU General Public License 18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 28 28 import optparse 29 29 30 from pkipplib import pkipplib 31 32 if __name__ == "__main__" : 30 from pkipplib import pkipplib 31 32 if __name__ == "__main__" : 33 33 try : 34 34 locale.setlocale(locale.LC_ALL, "") … … 40 40 gettext.NullTranslations().install() 41 41 parser = optparse.OptionParser(usage="pksubscribe [options] [subscriptions ids]") 42 parser.add_option("-v", "--version", 43 action="store_true", 42 parser.add_option("-v", "--version", 43 action="store_true", 44 44 dest="version", 45 45 help=_("show pksubscribe's version number and exit.")) 46 parser.add_option("-c", "--cups", 47 default="http://localhost:631", 46 parser.add_option("-c", "--cups", 47 default="http://localhost:631", 48 48 dest="cups", 49 49 help=_("the CUPS server to connect to. Defaults to http://localhost:631")) 50 parser.add_option("-d", "--debug", 51 action="store_true", 50 parser.add_option("-d", "--debug", 51 action="store_true", 52 52 dest="debug", 53 53 help=_("activate debug mode.")) 54 parser.add_option("-X", "--delete", 55 action="store_true", 54 parser.add_option("-X", "--delete", 55 action="store_true", 56 56 dest="delete", 57 57 help=_("deletes subscriptions.")) 58 parser.add_option("-p", "--printer", 58 parser.add_option("-p", "--printer", 59 59 dest="printer", 60 60 help=_("the printer's name for a printer subscription.")) 61 parser.add_option("-j", "--job", 62 type="int", 61 parser.add_option("-j", "--job", 62 type="int", 63 63 dest="job", 64 64 help=_("the job's id for a job subscripition.")) 65 parser.add_option("-r", "--recipient", 65 parser.add_option("-r", "--recipient", 66 66 dest="recipient", 67 67 help=_("the recipient's uri.")) 68 parser.add_option("-C", "--charset", 68 parser.add_option("-C", "--charset", 69 69 dest="charset", 70 70 help=_("the charset to use in notifications sent for this subscription.")) 71 parser.add_option("-L", "--language", 71 parser.add_option("-L", "--language", 72 72 dest="language", 73 73 help=_("the language to use in notifications sent for this subscription.")) 74 parser.add_option("-u", "--userdata", 74 parser.add_option("-u", "--userdata", 75 75 dest="userdata", 76 76 help=_("the user's data to use in notifications for this subscription.")) 77 parser.add_option("-U", "--username", 77 parser.add_option("-U", "--username", 78 78 dest="username", 79 79 help=_("the user's name to use when connecting to the CUPS server.")) 80 parser.add_option("-W", "--password", 80 parser.add_option("-W", "--password", 81 81 dest="password", 82 82 help=_("the user's password to use when connecting to the CUPS server.")) 83 parser.add_option("-E", "--events", 83 parser.add_option("-E", "--events", 84 84 dest="events", 85 85 help=_("a comma separated list of events to subscribe to.")) 86 parser.add_option("-P", "--pullmethod", 86 parser.add_option("-P", "--pullmethod", 87 87 dest="pullmethod", 88 88 help=_("the optional pull method's name.")) … … 91 91 dest="duration", 92 92 help=_("the duration of the subscription.")) 93 parser.add_option("-I", "--interval", 93 parser.add_option("-I", "--interval", 94 94 type="int", 95 95 dest="interval", 96 96 help=_("the time interval of the subscription.")) 97 97 98 98 (options, arguments) = parser.parse_args() 99 99 if options.version : … … 102 102 if not options.events and not options.delete : 103 103 sys.stderr.write(_("You MUST pass a list of events to subscribe to.\n")) 104 elif not options.recipient and not options.delete : 104 elif not options.recipient and not options.delete : 105 105 sys.stderr.write(_("You MUST pass a recipient for the subscription.\n")) 106 elif options.delete and not arguments : 106 elif options.delete and not arguments : 107 107 sys.stderr.write(_("You MUST pass a subscriptions ids at the end of your command line.\n")) 108 else : 108 else : 109 109 cups = pkipplib.CUPS(options.cups, 110 110 options.username, … … 112 112 debug=options.debug) 113 113 baseurl = options.cups.replace("http://", "ipp://") 114 if baseurl.endswith(":631") : 114 if baseurl.endswith(":631") : 115 115 baseurl = baseurl[:-4] 116 116 if options.printer : 117 117 url = "%s/printers/%s" % (baseurl, options.printer) 118 elif options.job : 118 elif options.job : 119 119 url = "%s/jobs/%i" % (baseurl, options.job) 120 else : 120 else : 121 121 url = baseurl 122 if not options.delete : 122 if not options.delete : 123 123 answer = cups.createSubscription(url, 124 124 [e.strip() for e in options.events.split(",")], … … 131 131 timeinterval=options.interval, 132 132 jobid=options.job) 133 try : 133 try : 134 134 subscriptionid = answer.subscription["notify-subscription-id"][0][1] 135 135 except KeyError : 136 136 sys.stderr.write("%s\n" % answer.operation["status-message"][0][1]) 137 else : 137 else : 138 138 print _("Subscription %i registered.") % subscriptionid 139 else : 139 else : 140 140 for subid in [int(arg) for arg in arguments] : 141 141 answer = cups.cancelSubscription(url, subid, options.job) … … 143 143 try : 144 144 error = answer.operation["status-message"][0][1] 145 except KeyError : 145 except KeyError : 146 146 print _("Subscription %i cancelled.") % subid 147 147 else : 148 148 sys.stderr.write("%s\n" % error) 149 else : 149 else : 150 150 sys.stderr.write(_("Incorrect answer (None)\n")) 151 151 -
pkipplib/trunk/clean.sh
r45 r3437 1 #! /bin/sh1 y#! /bin/sh 2 2 # 3 3 # pkipplib : IPP support for Python … … 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/>. -
pkipplib/trunk/MANIFEST.in
r36 r3437 1 include README COPYING NEWS MANIFEST.in clean.sh bin/pksubscribe1 yinclude README COPYING NEWS MANIFEST.in clean.sh bin/pksubscribe 2 2 recursive-include notifiers * -
pkipplib/trunk/notifiers/samplenotifier
r45 r3437 1 1 #! /usr/bin/env python 2 # -*- coding: UTF-8 -*-2 # -*- coding: utf-8 -*- 3 3 # 4 4 # pkipplib : IPP and CUPS support for Python … … 9 9 # the Free Software Foundation, either version 3 of the License, or 10 10 # (at your option) any later version. 11 # 11 # 12 12 # This program is distributed in the hope that it will be useful, 13 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 15 # GNU General Public License for more details. 16 # 16 # 17 17 # You should have received a copy of the GNU General Public License 18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 38 38 # First thing we do is put stdin in non-blocking mode. 39 39 fd = sys.stdin.fileno() 40 fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, 40 fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, 41 41 fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK) 42 42 43 43 # then we read the notification CUPS sent us to our stdin 44 44 notification = pkipplib.IPPRequest(sys.stdin.read()) 45 45 46 46 # now we parse it 47 47 notification.parse() 48 48 49 49 # then we act one way or another, depending on the event received. 50 50 event = notification.event_notification["notify-subscribed-event"][0][1] … … 53 53 if event.endswith("-added") : 54 54 action = "add" 55 else : 55 else : 56 56 action = "delete" 57 57 os.system('/usr/bin/pkprinters --%s "%s"' % (action, printername)) -
pkipplib/trunk/pkipplib/__init__.py
r45 r3437 1 # -*- coding: UTF-8 -*-1 # -*- coding: utf-8 -*- 2 2 # 3 3 # pkipplib : IPP and CUPS support for Python … … 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/>. -
pkipplib/trunk/pkipplib/pkipplib.py
r45 r3437 1 1 #! /usr/bin/env python 2 # -*- coding: UTF-8 -*-2 # -*- coding: utf-8 -*- 3 3 # 4 4 # pkipplib : IPP and CUPS support for Python … … 9 9 # the Free Software Foundation, either version 3 of the License, or 10 10 # (at your option) any later version. 11 # 11 # 12 12 # This program is distributed in the hope that it will be useful, 13 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 15 # GNU General Public License for more details. 16 # 16 # 17 17 # You should have received a copy of the GNU General Public License 18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 238 238 IPP_MULTIPLE_JOBS_NOT_SUPPORTED = 0x0509 239 239 IPP_PRINTER_IS_DEACTIVATED = 0x50a 240 240 241 241 CUPS_PRINTER_LOCAL = 0x0000 242 242 CUPS_PRINTER_CLASS = 0x0001 … … 265 265 CUPS_PRINTER_COMMANDS = 0x8000 266 266 CUPS_PRINTER_OPTIONS = 0xe6ff 267 268 267 268 269 269 class IPPError(Exception) : 270 270 """An exception for IPP related stuff.""" … … 282 282 self.request = request 283 283 self.name = name 284 284 285 285 def __setitem__(self, key, value) : 286 286 """Appends the value to the real attribute.""" … … 293 293 attribute[j][1].append(value) 294 294 return 295 attribute.append((key, [value])) 296 295 attribute.append((key, [value])) 296 297 297 def __getitem__(self, key) : 298 298 """Returns an attribute's value.""" … … 307 307 if answer : 308 308 return answer 309 raise KeyError, key 310 309 raise KeyError, key 310 311 311 class IPPRequest : 312 312 """A class for IPP requests.""" 313 313 attributes_types = ("operation", "job", "printer", "unsupported", \ 314 314 "subscription", "event_notification") 315 def __init__(self, data="", version=IPP_VERSION, 315 def __init__(self, data="", version=IPP_VERSION, 316 316 operation_id=None, \ 317 317 request_id=None, \ 318 318 debug=False) : 319 319 """Initializes an IPP Message object. 320 320 321 321 Parameters : 322 322 323 323 data : the complete IPP Message's content. 324 324 debug : a boolean value to output debug info on stderr. … … 327 327 self._data = data 328 328 self.parsed = False 329 329 330 330 # Initializes message 331 self.setVersion(version) 331 self.setVersion(version) 332 332 self.setOperationId(operation_id) 333 333 self.setRequestId(request_id) 334 334 self.data = "" 335 335 336 336 for attrtype in self.attributes_types : 337 337 setattr(self, "_%s_attributes" % attrtype, [[]]) 338 339 # Initialize tags 338 339 # Initialize tags 340 340 self.tags = [ None ] * 256 # by default all tags reserved 341 341 342 342 # Delimiter tags 343 343 self.tags[0x01] = "operation-attributes-tag" … … 348 348 self.tags[0x06] = "subscription-attributes-tag" 349 349 self.tags[0x07] = "event_notification-attributes-tag" 350 350 351 351 # out of band values 352 352 self.tags[0x10] = "unsupported" … … 357 357 self.tags[0x16] = "delete-attribute" 358 358 self.tags[0x17] = "admin-define" 359 359 360 360 # integer values 361 361 self.tags[0x20] = "generic-integer" … … 363 363 self.tags[0x22] = "boolean" 364 364 self.tags[0x23] = "enum" 365 365 366 366 # octetString 367 367 self.tags[0x30] = "octetString-with-an-unspecified-format" … … 373 373 self.tags[0x36] = "nameWithLanguage" 374 374 self.tags[0x37] = "endCollection" 375 375 376 376 # character strings 377 377 self.tags[0x40] = "generic-character-string" … … 385 385 self.tags[0x49] = "mimeMediaType" 386 386 self.tags[0x4a] = "memberAttrName" 387 387 388 388 # Reverse mapping to generate IPP messages 389 389 self.tagvalues = {} … … 392 392 if value is not None : 393 393 self.tagvalues[value] = i 394 395 def __getattr__(self, name) : 394 395 def __getattr__(self, name) : 396 396 """Fakes attribute access.""" 397 397 if name in self.attributes_types : … … 399 399 else : 400 400 raise AttributeError, name 401 402 def __str__(self) : 401 402 def __str__(self) : 403 403 """Returns the parsed IPP message in a readable form.""" 404 404 if not self.parsed : … … 414 414 for (name, value) in attribute : 415 415 mybuffer.append(" %s : %s" % (name, value)) 416 if self.data : 416 if self.data : 417 417 mybuffer.append("IPP datas : %s" % repr(self.data)) 418 418 return "\n".join(mybuffer) 419 420 def logDebug(self, msg) : 419 420 def logDebug(self, msg) : 421 421 """Prints a debug message.""" 422 422 if self.debug : 423 423 sys.stderr.write("%s\n" % msg) 424 424 sys.stderr.flush() 425 425 426 426 def setVersion(self, version) : 427 427 """Sets the request's operation id.""" … … 432 432 if len(version) == 2 : # 2-tuple 433 433 self.version = version 434 else : 434 else : 435 435 try : 436 436 self.version = [int(p) for p in str(float(version)).split(".")] 437 437 except : 438 438 self.version = [int(p) for p in IPP_VERSION.split(".")] 439 440 def setOperationId(self, opid) : 439 440 def setOperationId(self, opid) : 441 441 """Sets the request's operation id.""" 442 442 self.operation_id = opid 443 444 def setRequestId(self, reqid) : 443 444 def setRequestId(self, reqid) : 445 445 """Sets the request's request id.""" 446 446 self.request_id = reqid 447 448 def dump(self) : 447 448 def dump(self) : 449 449 """Generates an IPP Message. 450 450 451 451 Returns the message as a string of text. 452 """ 452 """ 453 453 mybuffer = [] 454 454 if None not in (self.version, self.operation_id) : … … 468 468 mybuffer.append(attrname) 469 469 nameprinted = 1 470 else : 470 else : 471 471 mybuffer.append(pack(">H", 0)) 472 472 if vtype in ("integer", "enum") : … … 476 476 mybuffer.append(pack(">H", 1)) 477 477 mybuffer.append(chr(val)) 478 else : 478 else : 479 479 mybuffer.append(pack(">H", len(val))) 480 480 mybuffer.append(val) 481 481 mybuffer.append(chr(self.tagvalues["end-of-attributes-tag"])) 482 mybuffer.append(self.data) 482 mybuffer.append(self.data) 483 483 return "".join(mybuffer) 484 484 485 485 def parse(self) : 486 486 """Parses an IPP Request. 487 487 488 488 NB : Only a subset of RFC2910 is implemented. 489 489 """ 490 490 self._curname = None 491 491 self._curattributes = None 492 492 493 493 try : 494 494 self.setVersion((ord(self._data[0]), ord(self._data[1]))) … … 509 509 self.position -= 1 510 510 continue 511 oldtag = tag 511 oldtag = tag 512 512 tag = ord(self._data[self.position]) 513 513 if tag == oldtag : … … 515 515 except IndexError : 516 516 raise IPPError, "Unexpected end of IPP message." 517 518 self.data = self._data[self.position+1:] 517 518 self.data = self._data[self.position+1:] 519 519 self.parsed = True 520 521 def parseTag(self) : 520 521 def parseTag(self) : 522 522 """Extracts information from an IPP tag.""" 523 523 pos = self.position … … 528 528 if not namelength : 529 529 name = self._curname 530 else : 530 else : 531 531 posend += namelength 532 532 self._curname = name = self._data[pos2:posend] … … 537 537 if tagtype in ("integer", "enum") : 538 538 value = unpack(">I", value)[0] 539 elif tagtype == "boolean" : 539 elif tagtype == "boolean" : 540 540 value = ord(value) 541 try : 541 try : 542 542 (oldname, oldval) = self._curattributes[-1][-1] 543 543 if oldname == name : 544 544 oldval.append((tagtype, value)) 545 else : 545 else : 546 546 raise IndexError 547 except IndexError : 547 except IndexError : 548 548 self._curattributes[-1].append((name, [(tagtype, value)])) 549 549 self.logDebug("%s(%s) : %s" % (name, tagtype, value)) 550 550 return posend - self.position 551 552 def operation_attributes_tag(self) : 551 552 def operation_attributes_tag(self) : 553 553 """Indicates that the parser enters into an operation-attributes-tag group.""" 554 554 self._curattributes = self._operation_attributes 555 555 return self.parseTag() 556 557 def job_attributes_tag(self) : 556 557 def job_attributes_tag(self) : 558 558 """Indicates that the parser enters into a job-attributes-tag group.""" 559 559 self._curattributes = self._job_attributes 560 560 return self.parseTag() 561 562 def printer_attributes_tag(self) : 561 562 def printer_attributes_tag(self) : 563 563 """Indicates that the parser enters into a printer-attributes-tag group.""" 564 564 self._curattributes = self._printer_attributes 565 565 return self.parseTag() 566 567 def unsupported_attributes_tag(self) : 566 567 def unsupported_attributes_tag(self) : 568 568 """Indicates that the parser enters into an unsupported-attributes-tag group.""" 569 569 self._curattributes = self._unsupported_attributes 570 570 return self.parseTag() 571 572 def subscription_attributes_tag(self) : 571 572 def subscription_attributes_tag(self) : 573 573 """Indicates that the parser enters into a subscription-attributes-tag group.""" 574 574 self._curattributes = self._subscription_attributes 575 575 return self.parseTag() 576 577 def event_notification_attributes_tag(self) : 576 577 def event_notification_attributes_tag(self) : 578 578 """Indicates that the parser enters into an event-notification-attributes-tag group.""" 579 579 self._curattributes = self._event_notification_attributes 580 580 return self.parseTag() 581 582 581 582 583 583 class CUPS : 584 584 """A class for a CUPS instance.""" … … 589 589 if self.url.endswith("/") : 590 590 self.url = self.url[:-1] 591 else : 591 else : 592 592 self.url = self.getDefaultURL() 593 593 594 594 # If no username or password, use the ones set by CUPS, if any 595 595 self.username = username or os.environ.get("AUTH_USERNAME") 596 596 self.password = password or os.environ.get("AUTH_PASSWORD") 597 597 598 598 self.charset = charset 599 599 self.language = language … … 602 602 self.lastErrorMessage = None 603 603 self.requestId = None 604 605 def getDefaultURL(self) : 604 605 def getDefaultURL(self) : 606 606 """Builds a default URL.""" 607 607 # TODO : encryption methods. … … 613 613 return "http://localhost:%s" % port 614 614 #return "socket:%s" % server # TODO : make this work ! 615 else : 615 else : 616 616 return "http://%s:%s" % (server, port) 617 617 618 618 def identifierToURI(self, service, ident) : 619 619 """Transforms an identifier into a particular URI depending on requested service.""" … … 621 621 service, 622 622 ident) 623 624 def nextRequestId(self) : 623 624 def nextRequestId(self) : 625 625 """Increments the current request id and returns the new value.""" 626 626 try : 627 627 self.requestId += 1 628 except TypeError : 628 except TypeError : 629 629 self.requestId = 1 630 630 return self.requestId 631 631 632 632 def newRequest(self, operationid=None) : 633 633 """Generates a new empty request.""" … … 641 641 req.operation["requesting-user-name"] = ("nameWithoutLanguage", self.username) 642 642 return req 643 643 644 644 def doRequest(self, req, url=None) : 645 645 """Sends a request to the CUPS server. 646 646 returns a new IPPRequest object, containing the parsed answer. 647 """ 647 """ 648 648 url = url or self.url 649 649 connection = urllib2.Request(url=url, \ … … 657 657 self.username, \ 658 658 self.password or "") 659 authhandler = urllib2.HTTPBasicAuthHandler(pwmanager) 659 authhandler = urllib2.HTTPBasicAuthHandler(pwmanager) 660 660 opener = urllib2.build_opener(authhandler) 661 661 urllib2.install_opener(opener) 662 else : # TODO : also do this in the 'if' part 663 if url.startswith("socket:") : 662 else : # TODO : also do this in the 'if' part 663 if url.startswith("socket:") : 664 664 class SocketHandler(urllib2.HTTPHandler) : 665 665 """A class to handle IPP connections over an Unix domain socket.""" … … 675 675 sys.stderr.write("Opened [%s]\n" % req.get_selector()) 676 676 return s.makefile(mode="r+b") 677 678 opener = urllib2.build_opener(SocketHandler()) 677 678 opener = urllib2.build_opener(SocketHandler()) 679 679 urllib2.install_opener(opener) 680 680 sys.stderr.write("Opener installed\n") 681 self.lastError = None 681 self.lastError = None 682 682 self.lastErrorMessage = None 683 try : 683 try : 684 684 response = urllib2.urlopen(connection) 685 except (urllib2.URLError, urllib2.HTTPError, socket.error), error : 685 except (urllib2.URLError, urllib2.HTTPError, socket.error), error : 686 686 self.lastError = error 687 687 self.lastErrorMessage = str(error) 688 688 return None 689 else : 689 else : 690 690 bytes = [] 691 691 try : … … 694 694 try : 695 695 byte = response.read(1) 696 except AttributeError : 696 except AttributeError : 697 697 byte = response.recv(1) 698 698 if not byte : 699 699 break 700 else : 700 else : 701 701 bytes.append(byte) 702 702 except socket.error : … … 711 711 else : 712 712 return None 713 714 def getPPD(self, queuename) : 713 714 def getPPD(self, queuename) : 715 715 """Retrieves the PPD for a particular queuename.""" 716 716 req = self.newRequest(IPP_GET_PRINTER_ATTRIBUTES) … … 719 719 req.operation["requested-attributes"] = ("nameWithoutLanguage", attrib) 720 720 return self.doRequest(req) # TODO : get the PPD from the actual print server 721 721 722 722 def getDefault(self) : 723 723 """Retrieves CUPS' default printer.""" 724 724 return self.doRequest(self.newRequest(CUPS_GET_DEFAULT)) 725 726 def getJobAttributes(self, jobid) : 725 726 def getJobAttributes(self, jobid) : 727 727 """Retrieves a print job's attributes.""" 728 728 req = self.newRequest(IPP_GET_JOB_ATTRIBUTES) 729 729 req.operation["job-uri"] = ("uri", self.identifierToURI("jobs", jobid)) 730 730 return self.doRequest(req) 731 732 def getPrinters(self) : 731 732 def getPrinters(self) : 733 733 """Returns the list of print queues names.""" 734 734 req = self.newRequest(CUPS_GET_PRINTERS) … … 737 737 req.operation["printer-type-mask"] = ("enum", CUPS_PRINTER_CLASS) 738 738 return [printer[1] for printer in self.doRequest(req).printer["printer-name"]] 739 740 def getDevices(self) : 739 740 def getDevices(self) : 741 741 """Returns a list of devices as (deviceclass, deviceinfo, devicemakeandmodel, deviceuri) tuples.""" 742 742 answer = self.doRequest(self.newRequest(CUPS_GET_DEVICES)) … … 745 745 [d[1] for d in answer.printer["device-make-and-model"]], \ 746 746 [d[1] for d in answer.printer["device-uri"]]) 747 748 def getPPDs(self) : 747 748 def getPPDs(self) : 749 749 """Returns a list of PPDs as (ppdnaturallanguage, ppdmake, ppdmakeandmodel, ppdname) tuples.""" 750 750 answer = self.doRequest(self.newRequest(CUPS_GET_PPDS)) … … 753 753 [d[1] for d in answer.printer["ppd-make-and-model"]], \ 754 754 [d[1] for d in answer.printer["ppd-name"]]) 755 755 756 756 def createSubscription(self, uri, events=["all"], 757 757 userdata=None, … … 764 764 jobid=None) : 765 765 """Creates a job, printer or server subscription. 766 766 767 767 uri : the subscription's uri, e.g. ipp://server 768 768 events : a list of events to subscribe to, e.g. ["printer-added", "printer-deleted"] … … 774 774 timeinterval : the interval of time during notifications 775 775 jobid : the optional job id in case of a job subscription 776 """ 776 """ 777 777 if jobid is not None : 778 778 opid = IPP_CREATE_JOB_SUBSCRIPTION … … 785 785 for event in events : 786 786 req.subscription["notify-events"] = ("keyword", event) 787 if userdata is not None : 787 if userdata is not None : 788 788 req.subscription["notify-user-data"] = ("octetString-with-an-unspecified-format", userdata) 789 if recipient is not None : 789 if recipient is not None : 790 790 req.subscription["notify-recipient"] = ("uri", recipient) 791 791 if pullmethod is not None : … … 802 802 req.subscription["notify-job-id"] = ("integer", jobid) 803 803 return self.doRequest(req) 804 805 def cancelSubscription(self, uri, subscriptionid, jobid=None) : 804 805 def cancelSubscription(self, uri, subscriptionid, jobid=None) : 806 806 """Cancels a subscription. 807 807 808 808 uri : the subscription's uri. 809 809 subscriptionid : the subscription's id. … … 818 818 req.event_notification["notify-subscription-id"] = ("integer", subscriptionid) 819 819 return self.doRequest(req) 820 821 if __name__ == "__main__" : 820 821 if __name__ == "__main__" : 822 822 if (len(sys.argv) < 2) or (sys.argv[1] == "--debug") : 823 823 print "usage : python pkipplib.py /var/spool/cups/c00005 [--debug] (for example)\n" 824 else : 824 else : 825 825 infile = open(sys.argv[1], "rb") 826 826 filedata = infile.read() 827 827 infile.close() 828 828 829 829 msg = IPPRequest(filedata, debug=(sys.argv[-1]=="--debug")) 830 830 msg.parse() … … 832 832 msg2.parse() 833 833 filedata2 = msg2.dump() 834 834 835 835 if filedata == filedata2 : 836 836 print "Test OK : parsing original and parsing the output of the dump produce the same dump !" 837 837 print str(msg) 838 else : 838 else : 839 839 print "Test Failed !" 840 840 print str(msg) 841 841 print 842 842 print str(msg2) 843 843 -
pkipplib/trunk/pkipplib/version.py
r45 r3437 1 # -*- coding: UTF-8 -*-1 # -*- coding: utf-8 -*- 2 2 # 3 3 # pkipplib : IPP and CUPS support for Python … … 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/>. -
pkipplib/trunk/README
r3380 r3437 1 pkipplib : IPP and CUPS support for Python1 ypkipplib : IPP and CUPS support for Python 2 2 3 3 $Id$ … … 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/>. … … 29 29 and its instance methods. 30 30 31 Finally, a CUPS class can be leveraged to easily deal with CUPS 31 Finally, a CUPS class can be leveraged to easily deal with CUPS 32 32 print servers. 33 33 … … 42 42 43 43 1 - Grab the latest version of this library from : 44 44 45 45 http://www.pykota.com/software/pkipplib/ 46 47 2 - Extract the tarball : 48 46 47 2 - Extract the tarball : 48 49 49 $ tar -zxf pkipplib-x.yy.tar.gz 50 51 3 - Install the Python module : 52 50 51 3 - Install the Python module : 52 53 53 $ cd pkipplib-x.yy 54 54 $ python setup.py install … … 78 78 print request 79 79 80 # Access one of the job's attributes directly 80 # Access one of the job's attributes directly 81 81 print request.job["job-name"] 82 82 --- CUT --- … … 119 119 # Lower level API : 120 120 request = cups.newRequest(pkipplib.IPP_GET_PRINTER_ATTRIBUTES) 121 request.operation["printer-uri"] = ("uri", 121 request.operation["printer-uri"] = ("uri", 122 122 cups.identifierToURI("printers", "HP2100")) 123 for attribute in ("printer-uri-supported", 123 for attribute in ("printer-uri-supported", 124 124 "printer-type", 125 125 "member-uris") : 126 # IMPORTANT : here, despite the unusual syntax, we append to 126 # IMPORTANT : here, despite the unusual syntax, we append to 127 127 # the list of requested attributes : 128 128 request.operation["requested-attributes"] = ("nameWithoutLanguage", attribute) 129 130 # Sends this request to the CUPS server 129 130 # Sends this request to the CUPS server 131 131 answer = cups.doRequest(request) 132 132 … … 140 140 141 141 pkipplib currently includes the following command line tools : 142 142 143 143 * pksubscribe : can create or delete IPP subscriptions. 144 144 145 145 See pksubscribe --help for details. 146 146 147 147 examples : 148 148 149 149 $ pksubscribe --cups http://localhost:631 \ 150 150 --events printer-added,printer-deleted \ 151 151 --recipient mynotifier \ 152 152 --duration 0 153 154 $ pksubscribe --username root \ 153 154 $ pksubscribe --username root \ 155 155 --password HacKMe \ 156 156 --delete 34 58 98 157 157 158 158 * samplenotifier : can handle printer-added and printer-deleted 159 159 notifications and automatically create or remove printers 160 160 from PyKota's database (just adapt the code for other needs). 161 161 162 162 samplenotifier is present in the notifiers/ directory, but 163 163 won't be installed automatically. You must put it into … … 165 165 166 166 In the future more command line tools will be included. 167 167 168 168 ======================================================================= 169 169 -
pkipplib/trunk/setup.py
r45 r3437 1 1 #! /usr/bin/env python 2 # -*- coding: UTF-8 -*-2 # -*- coding: utf-8 -*- 3 3 # 4 4 # pkipplib : IPP and CUPS support for Python … … 9 9 # the Free Software Foundation, either version 3 of the License, or 10 10 # (at your option) any later version. 11 # 11 # 12 12 # This program is distributed in the hope that it will be useful, 13 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 15 # GNU General Public License for more details. 16 # 16 # 17 17 # You should have received a copy of the GNU General Public License 18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. -
pkipplib/trunk/tests/printjob.py
r46 r3437 1 #! /usr/bin/env python1 y#! /usr/bin/env python 2 2 # -*- coding: ISO-8859-15 -*- 3 3 # … … 9 9 # the Free Software Foundation, either version 3 of the License, or 10 10 # (at your option) any later version. 11 # 11 # 12 12 # This program is distributed in the hope that it will be useful, 13 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 15 # GNU General Public License for more details. 16 # 16 # 17 17 # You should have received a copy of the GNU General Public License 18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. -
pkipplib/trunk/tests/test.py
r45 r3437 1 1 #! /usr/bin/env python 2 # -*- coding: UTF-8 -*-2 # -*- coding: utf-8 -*- 3 3 # 4 4 # pkipplib : IPP and CUPS support for Python … … 9 9 # the Free Software Foundation, either version 3 of the License, or 10 10 # (at your option) any later version. 11 # 11 # 12 12 # This program is distributed in the hope that it will be useful, 13 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 15 # GNU General Public License for more details. 16 # 16 # 17 17 # You should have received a copy of the GNU General Public License 18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 23 23 24 24 sys.path.insert(0, "../pkipplib") 25 import pkipplib 26 25 import pkipplib 26 27 27 cups = pkipplib.CUPS() 28 28 answer = cups.createSubscription("ipp://localhost/", ["printer-added", "printer-deleted"],