- Location:
- /pkipplib/trunk
- Files:
-
- 1 removed
- 4 modified
Legend:
- Unmodified
- Added
- Removed
-
/pkipplib/trunk/tests/test.py
r28 r13 26 26 import pkipplib 27 27 28 cups = pkipplib.CUPS() 29 answer = cups.createSubscription("ipp://localhost/", ["printer-added", "printer-deleted"], 30 userdata="samplenotifier:blah", 31 recipient="samplenotifier", 32 charset="utf-8") 28 cups = pkipplib.CUPS() 29 answer = cups.getPPD("HL6050DN") 30 #answer = cups.getJobAttributes(566) 33 31 print answer 34 35 32 #print answer.operation["attributes-charset"] 36 33 -
/pkipplib/trunk/pkipplib/pkipplib.py
r30 r18 24 24 25 25 import sys 26 import os27 26 import urllib2 28 import socket29 27 from struct import pack, unpack 30 28 … … 238 236 IPP_PRINTER_IS_DEACTIVATED = 0x50a 239 237 240 CUPS_PRINTER_LOCAL = 0x0000241 CUPS_PRINTER_CLASS = 0x0001242 CUPS_PRINTER_REMOTE = 0x0002243 CUPS_PRINTER_BW = 0x0004244 CUPS_PRINTER_COLOR = 0x0008245 CUPS_PRINTER_DUPLEX = 0x0010246 CUPS_PRINTER_STAPLE = 0x0020247 CUPS_PRINTER_COPIES = 0x0040248 CUPS_PRINTER_COLLATE = 0x0080249 CUPS_PRINTER_PUNCH = 0x0100250 CUPS_PRINTER_COVER = 0x0200251 CUPS_PRINTER_BIND = 0x0400252 CUPS_PRINTER_SORT = 0x0800253 CUPS_PRINTER_SMALL = 0x1000254 CUPS_PRINTER_MEDIUM = 0x2000255 CUPS_PRINTER_LARGE = 0x4000256 CUPS_PRINTER_VARIABLE = 0x8000257 CUPS_PRINTER_IMPLICIT = 0x1000258 CUPS_PRINTER_DEFAULT = 0x2000259 CUPS_PRINTER_FAX = 0x4000260 CUPS_PRINTER_REJECTING = 0x8000261 CUPS_PRINTER_DELETE = 0x1000262 CUPS_PRINTER_NOT_SHARED = 0x2000263 CUPS_PRINTER_AUTHENTICATED = 0x4000264 CUPS_PRINTER_COMMANDS = 0x8000265 CUPS_PRINTER_OPTIONS = 0xe6ff266 267 268 238 class IPPError(Exception) : 269 239 """An exception for IPP related stuff.""" … … 296 266 def __getitem__(self, key) : 297 267 """Returns an attribute's value.""" 298 answer = []299 268 attributeslist = getattr(self.request, "_%s_attributes" % self.name) 300 269 for i in range(len(attributeslist)) : … … 303 272 (attrname, attrvalue) = attribute[j] 304 273 if attrname == key : 305 answer.extend(attrvalue) 306 if answer : 307 return answer 274 return attrvalue 308 275 raise KeyError, key 309 276 … … 315 282 operation_id=None, \ 316 283 request_id=None, \ 284 url = "http://localhost:631", \ 285 username = None, \ 286 password = None, \ 317 287 debug=False) : 318 288 """Initializes an IPP Message object. … … 323 293 debug : a boolean value to output debug info on stderr. 324 294 """ 295 self.url = url 296 self.username = username 297 self.password = password 325 298 self.debug = debug 326 299 self._data = data … … 346 319 self.tags[0x05] = "unsupported-attributes-tag" 347 320 self.tags[0x06] = "subscription-attributes-tag" 348 self.tags[0x07] = "event _notification-attributes-tag"321 self.tags[0x07] = "event-notification-attributes-tag" 349 322 350 323 # out of band values … … 445 418 self.request_id = reqid 446 419 420 def nextRequestId(self) : 421 """Increments the current request id and returns the new value.""" 422 try : 423 self.request_id += 1 424 except TypeError : 425 self.request_id = 1 426 return self.request_id 427 447 428 def dump(self) : 448 429 """Generates an IPP Message. … … 452 433 mybuffer = [] 453 434 if None not in (self.version, self.operation_id) : 435 if self.request_id is None : 436 self.nextRequestId() 454 437 mybuffer.append(chr(self.version[0]) + chr(self.version[1])) 455 438 mybuffer.append(pack(">H", self.operation_id)) 456 mybuffer.append(pack(">I", self.request_id or 1))439 mybuffer.append(pack(">I", self.request_id)) 457 440 for attrtype in self.attributes_types : 458 441 for attribute in getattr(self, "_%s_attributes" % attrtype) : … … 495 478 self.position = 8 496 479 endofattributes = self.tagvalues["end-of-attributes-tag"] 497 maxdelimiter = self.tagvalues["event _notification-attributes-tag"]480 maxdelimiter = self.tagvalues["event-notification-attributes-tag"] 498 481 nulloffset = lambda : 0 499 482 try : … … 579 562 return self.parseTag() 580 563 564 def doRequest(self, url=None, username=None, password=None, samerequestid=False) : 565 """Sends the current request to the URL. 566 NB : only ipp:// URLs are currently unsupported so use 567 either http://host:631/ or https://host:443 instead... 568 569 returns a new IPPRequest object, containing the parsed answer. 570 """ 571 if not samerequestid : 572 self.nextRequestId() 573 cx = urllib2.Request(url=url or self.url or "http://localhost:631/", 574 data=self.dump()) 575 cx.add_header("Content-Type", "application/ipp") 576 response = urllib2.urlopen(cx) 577 datas = response.read() 578 ippresponse = IPPRequest(datas) 579 ippresponse.parse() 580 return ippresponse 581 581 582 582 583 class CUPS : 583 584 """A class for a CUPS instance.""" 584 def __init__(self, url= None, username=None, password=None, charset="utf-8", language="en-us", debug=False) :585 def __init__(self, url="http://localhost:631", username=None, password=None, charset="utf-8", language="en-us") : 585 586 """Initializes the CUPS instance.""" 586 if url is not None : 587 self.url = url.replace("ipp://", "http://") 588 if self.url.endswith("/") : 589 self.url = self.url[:-1] 590 else : 591 self.url = self.getDefaultURL() 587 self.url = url 592 588 self.username = username 593 589 self.password = password 594 590 self.charset = charset 595 591 self.language = language 596 self.debug = debug 597 self.lastError = None 598 self.lastErrorMessage = None 599 self.requestId = None 600 601 def getDefaultURL(self) : 602 """Builds a default URL.""" 603 # TODO : encryption methods. 604 server = os.environ.get("CUPS_SERVER") or "localhost" 605 port = os.environ.get("IPP_PORT") or 631 606 if server.startswith("/") : 607 # it seems it's a unix domain socket. 608 # we can't handle this right now, so we use the default instead. 609 return "http://localhost:%s" % port 610 else : 611 return "http://%s:%s" % (server, port) 612 592 613 593 def identifierToURI(self, service, ident) : 614 594 """Transforms an identifier into a particular URI depending on requested service.""" … … 617 597 ident) 618 598 619 def nextRequestId(self) :620 """Increments the current request id and returns the new value."""621 try :622 self.requestId += 1623 except TypeError :624 self.requestId = 1625 return self.requestId626 627 599 def newRequest(self, operationid=None) : 628 600 """Generates a new empty request.""" 629 601 if operationid is not None : 630 602 req = IPPRequest(operation_id=operationid, \ 631 request_id=self.nextRequestId(), \ 632 debug=self.debug) 603 url=self.url, \ 604 username=self.username, \ 605 password=self.password) 633 606 req.operation["attributes-charset"] = ("charset", self.charset) 634 607 req.operation["attributes-natural-language"] = ("naturalLanguage", self.language) 635 608 return req 636 609 637 def doRequest(self, req, url=None) : 638 """Sends a request to the CUPS server. 639 returns a new IPPRequest object, containing the parsed answer. 640 """ 641 connexion = urllib2.Request(url=url or self.url, \ 642 data=req.dump()) 643 connexion.add_header("Content-Type", "application/ipp") 644 if self.username : 645 pwmanager = urllib2.HTTPPasswordMgrWithDefaultRealm() 646 pwmanager.add_password(None, \ 647 "%s%s" % (connexion.get_host(), connexion.get_selector()), \ 648 self.username, \ 649 self.password or "") 650 authhandler = urllib2.HTTPBasicAuthHandler(pwmanager) 651 opener = urllib2.build_opener(authhandler) 652 urllib2.install_opener(opener) 653 self.lastError = None 654 self.lastErrorMessage = None 655 try : 656 response = urllib2.urlopen(connexion) 657 except (urllib2.URLError, urllib2.HTTPError, socket.error), error : 658 self.lastError = error 659 self.lastErrorMessage = str(error) 660 return None 661 else : 662 datas = response.read() 663 ippresponse = IPPRequest(datas) 664 ippresponse.parse() 665 return ippresponse 610 def getDefault(self) : 611 """Retrieves CUPS' default printer.""" 612 return self.newRequest(CUPS_GET_DEFAULT).doRequest() 666 613 614 def getJobAttributes(self, jobid) : 615 """Retrieves a print job's attributes.""" 616 req = self.newRequest(IPP_GET_JOB_ATTRIBUTES) 617 req.operation["job-uri"] = ("uri", self.identifierToURI("jobs", jobid)) 618 return req.doRequest() 619 667 620 def getPPD(self, queuename) : 668 621 """Retrieves the PPD for a particular queuename.""" … … 671 624 for attrib in ("printer-uri-supported", "printer-type", "member-uris") : 672 625 req.operation["requested-attributes"] = ("nameWithoutLanguage", attrib) 673 return self.doRequest(req) # TODO : get the PPD from the actual print server 674 675 def getDefault(self) : 676 """Retrieves CUPS' default printer.""" 677 return self.doRequest(self.newRequest(CUPS_GET_DEFAULT)) 678 679 def getJobAttributes(self, jobid) : 680 """Retrieves a print job's attributes.""" 681 req = self.newRequest(IPP_GET_JOB_ATTRIBUTES) 682 req.operation["job-uri"] = ("uri", self.identifierToURI("jobs", jobid)) 683 return self.doRequest(req) 684 685 def getPrinters(self) : 686 """Returns the list of print queues names.""" 687 req = self.newRequest(CUPS_GET_PRINTERS) 688 req.operation["requested-attributes"] = ("keyword", "printer-name") 689 req.operation["printer-type"] = ("enum", 0) 690 req.operation["printer-type-mask"] = ("enum", CUPS_PRINTER_CLASS) 691 return [printer[1] for printer in self.doRequest(req).printer["printer-name"]] 692 693 def getDevices(self) : 694 """Returns a list of devices as (deviceclass, deviceinfo, devicemakeandmodel, deviceuri) tuples.""" 695 answer = self.doRequest(self.newRequest(CUPS_GET_DEVICES)) 696 return zip([d[1] for d in answer.printer["device-class"]], \ 697 [d[1] for d in answer.printer["device-info"]], \ 698 [d[1] for d in answer.printer["device-make-and-model"]], \ 699 [d[1] for d in answer.printer["device-uri"]]) 700 701 def getPPDs(self) : 702 """Returns a list of PPDs as (ppdnaturallanguage, ppdmake, ppdmakeandmodel, ppdname) tuples.""" 703 answer = self.doRequest(self.newRequest(CUPS_GET_PPDS)) 704 return zip([d[1] for d in answer.printer["ppd-natural-language"]], \ 705 [d[1] for d in answer.printer["ppd-make"]], \ 706 [d[1] for d in answer.printer["ppd-make-and-model"]], \ 707 [d[1] for d in answer.printer["ppd-name"]]) 708 709 def createSubscription(self, uri, events=["all"], 710 userdata=None, 711 recipient=None, 712 pullmethod=None, 713 charset=None, 714 naturallanguage=None, 715 leaseduration=None, 716 timeinterval=None, 717 jobid=None) : 718 """Creates a job, printer or server subscription.""" 719 if jobid is not None : 720 opid = IPP_CREATE_JOB_SUBSCRIPTION 721 uritype = "job-uri" 722 else : 723 opid = IPP_CREATE_PRINTER_SUBSCRIPTION 724 uritype = "printer-uri" 725 req = self.newRequest(opid) 726 req.operation[uritype] = ("uri", uri) 727 for event in events : 728 req.subscription["notify-events"] = ("keyword", event) 729 if userdata is not None : 730 req.subscription["notify-user-data"] = ("octetString-with-an-unspecified-format", userdata) 731 if recipient is not None : 732 req.subscription["notify-recipient"] = ("uri", recipient) 733 if pullmethod is not None : 734 req.subscription["notify-pull-method"] = ("keyword", pullmethod) 735 if charset is not None : 736 req.subscription["notify-charset"] = ("charset", charset) 737 if naturallanguage is not None : 738 req.subscription["notify-natural-language"] = ("naturalLanguage", naturallanguage) 739 if leaseduration is not None : 740 req.subscription["notify-lease-duration"] = ("integer", leaseduration) 741 if timeinterval is not None : 742 req.subscription["notify-time-interval"] = ("integer", timeinterval) 743 if jobid is not None : 744 req.subscription["notify-job-id"] = ("integer", jobid) 745 return self.doRequest(req) 746 747 def cancelSubscription(self, uri, subscriptionid, jobid=None) : 748 """Cancels a subscription.""" 749 req = self.newRequest(IPP_CANCEL_SUBSCRIPTION) 750 if jobid is not None : 751 uritype = "job-uri" 752 else : 753 uritype = "printer-uri" 754 req.operation[uritype] = ("uri", uri) 755 req.event_notification["notify-subscription-id"] = ("integer", subscriptionid) 756 return self.doRequest(req) 757 626 return req.doRequest() # TODO : get the PPD from the actual print server 627 628 758 629 if __name__ == "__main__" : 759 630 if (len(sys.argv) < 2) or (sys.argv[1] == "--debug") : 760 print "usage : python pkipplib.py /var/spool/cups/c00005 [--debug] (for example)\n"631 print "usage : python ipp.py /var/spool/cups/c00005 [--debug] (for example)\n" 761 632 else : 762 633 infile = open(sys.argv[1], "rb") 763 filedata = infile.read()634 data = infile.read() 764 635 infile.close() 765 636 766 m sg = IPPRequest(filedata, debug=(sys.argv[-1]=="--debug"))767 m sg.parse()768 m sg2 = IPPRequest(msg.dump())769 m sg2.parse()770 filedata2 = msg2.dump()771 772 if filedata == filedata2 :637 message = IPPRequest(data, debug=(sys.argv[-1]=="--debug")) 638 message.parse() 639 message2 = IPPRequest(message.dump()) 640 message2.parse() 641 data2 = message2.dump() 642 643 if data == data2 : 773 644 print "Test OK : parsing original and parsing the output of the dump produce the same dump !" 774 print str(m sg)645 print str(message) 775 646 else : 776 647 print "Test Failed !" 777 print str(m sg)648 print str(message) 778 649 print 779 print str(m sg2)780 650 print str(message2) 651 -
/pkipplib/trunk/pkipplib/version.py
r27 r14 21 21 # 22 22 23 __version__ = "0.0 7"23 __version__ = "0.03" 24 24 25 25 __doc__ = "pkipplib : IPP and CUPS support for Python." -
/pkipplib/trunk/README
r25 r19 109 109 from pkipplib import pkipplib 110 110 111 # Create a CUPS client instance 112 # cups = pkipplib.CUPS(url="http://server:631", username="john", password="blah!") 111 # By default, connects to http://localhost:631 113 112 cups = pkipplib.CUPS() 114 113 … … 130 129 131 130 # Sends this request to the CUPS server 132 answer = cups.doRequest(request)131 answer = request.doRequest() 133 132 134 133 # Print the answer as a string of text