- Location:
- /pkipplib/trunk
- Files:
-
- 2 added
- 4 modified
Legend:
- Unmodified
- Added
- Removed
-
/pkipplib/trunk/tests/test.py
r13 r28 26 26 import pkipplib 27 27 28 cups = pkipplib.CUPS() 29 answer = cups.getPPD("HL6050DN") 30 #answer = cups.getJobAttributes(566) 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") 31 33 print answer 34 32 35 #print answer.operation["attributes-charset"] 33 36 -
/pkipplib/trunk/pkipplib/pkipplib.py
r18 r30 24 24 25 25 import sys 26 import os 26 27 import urllib2 28 import socket 27 29 from struct import pack, unpack 28 30 … … 236 238 IPP_PRINTER_IS_DEACTIVATED = 0x50a 237 239 240 CUPS_PRINTER_LOCAL = 0x0000 241 CUPS_PRINTER_CLASS = 0x0001 242 CUPS_PRINTER_REMOTE = 0x0002 243 CUPS_PRINTER_BW = 0x0004 244 CUPS_PRINTER_COLOR = 0x0008 245 CUPS_PRINTER_DUPLEX = 0x0010 246 CUPS_PRINTER_STAPLE = 0x0020 247 CUPS_PRINTER_COPIES = 0x0040 248 CUPS_PRINTER_COLLATE = 0x0080 249 CUPS_PRINTER_PUNCH = 0x0100 250 CUPS_PRINTER_COVER = 0x0200 251 CUPS_PRINTER_BIND = 0x0400 252 CUPS_PRINTER_SORT = 0x0800 253 CUPS_PRINTER_SMALL = 0x1000 254 CUPS_PRINTER_MEDIUM = 0x2000 255 CUPS_PRINTER_LARGE = 0x4000 256 CUPS_PRINTER_VARIABLE = 0x8000 257 CUPS_PRINTER_IMPLICIT = 0x1000 258 CUPS_PRINTER_DEFAULT = 0x2000 259 CUPS_PRINTER_FAX = 0x4000 260 CUPS_PRINTER_REJECTING = 0x8000 261 CUPS_PRINTER_DELETE = 0x1000 262 CUPS_PRINTER_NOT_SHARED = 0x2000 263 CUPS_PRINTER_AUTHENTICATED = 0x4000 264 CUPS_PRINTER_COMMANDS = 0x8000 265 CUPS_PRINTER_OPTIONS = 0xe6ff 266 267 238 268 class IPPError(Exception) : 239 269 """An exception for IPP related stuff.""" … … 266 296 def __getitem__(self, key) : 267 297 """Returns an attribute's value.""" 298 answer = [] 268 299 attributeslist = getattr(self.request, "_%s_attributes" % self.name) 269 300 for i in range(len(attributeslist)) : … … 272 303 (attrname, attrvalue) = attribute[j] 273 304 if attrname == key : 274 return attrvalue 305 answer.extend(attrvalue) 306 if answer : 307 return answer 275 308 raise KeyError, key 276 309 … … 282 315 operation_id=None, \ 283 316 request_id=None, \ 284 url = "http://localhost:631", \285 username = None, \286 password = None, \287 317 debug=False) : 288 318 """Initializes an IPP Message object. … … 293 323 debug : a boolean value to output debug info on stderr. 294 324 """ 295 self.url = url296 self.username = username297 self.password = password298 325 self.debug = debug 299 326 self._data = data … … 319 346 self.tags[0x05] = "unsupported-attributes-tag" 320 347 self.tags[0x06] = "subscription-attributes-tag" 321 self.tags[0x07] = "event -notification-attributes-tag"348 self.tags[0x07] = "event_notification-attributes-tag" 322 349 323 350 # out of band values … … 418 445 self.request_id = reqid 419 446 420 def nextRequestId(self) :421 """Increments the current request id and returns the new value."""422 try :423 self.request_id += 1424 except TypeError :425 self.request_id = 1426 return self.request_id427 428 447 def dump(self) : 429 448 """Generates an IPP Message. … … 433 452 mybuffer = [] 434 453 if None not in (self.version, self.operation_id) : 435 if self.request_id is None :436 self.nextRequestId()437 454 mybuffer.append(chr(self.version[0]) + chr(self.version[1])) 438 455 mybuffer.append(pack(">H", self.operation_id)) 439 mybuffer.append(pack(">I", self.request_id ))456 mybuffer.append(pack(">I", self.request_id or 1)) 440 457 for attrtype in self.attributes_types : 441 458 for attribute in getattr(self, "_%s_attributes" % attrtype) : … … 478 495 self.position = 8 479 496 endofattributes = self.tagvalues["end-of-attributes-tag"] 480 maxdelimiter = self.tagvalues["event -notification-attributes-tag"]497 maxdelimiter = self.tagvalues["event_notification-attributes-tag"] 481 498 nulloffset = lambda : 0 482 499 try : … … 562 579 return self.parseTag() 563 580 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 use567 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 ippresponse581 582 581 583 582 class CUPS : 584 583 """A class for a CUPS instance.""" 585 def __init__(self, url= "http://localhost:631", username=None, password=None, charset="utf-8", language="en-us") :584 def __init__(self, url=None, username=None, password=None, charset="utf-8", language="en-us", debug=False) : 586 585 """Initializes the CUPS instance.""" 587 self.url = url 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() 588 592 self.username = username 589 593 self.password = password 590 594 self.charset = charset 591 595 self.language = language 592 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 593 613 def identifierToURI(self, service, ident) : 594 614 """Transforms an identifier into a particular URI depending on requested service.""" … … 597 617 ident) 598 618 619 def nextRequestId(self) : 620 """Increments the current request id and returns the new value.""" 621 try : 622 self.requestId += 1 623 except TypeError : 624 self.requestId = 1 625 return self.requestId 626 599 627 def newRequest(self, operationid=None) : 600 628 """Generates a new empty request.""" 601 629 if operationid is not None : 602 630 req = IPPRequest(operation_id=operationid, \ 603 url=self.url, \ 604 username=self.username, \ 605 password=self.password) 631 request_id=self.nextRequestId(), \ 632 debug=self.debug) 606 633 req.operation["attributes-charset"] = ("charset", self.charset) 607 634 req.operation["attributes-natural-language"] = ("naturalLanguage", self.language) 608 635 return req 609 636 610 def getDefault(self) : 611 """Retrieves CUPS' default printer.""" 612 return self.newRequest(CUPS_GET_DEFAULT).doRequest() 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 613 666 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 620 667 def getPPD(self, queuename) : 621 668 """Retrieves the PPD for a particular queuename.""" … … 624 671 for attrib in ("printer-uri-supported", "printer-type", "member-uris") : 625 672 req.operation["requested-attributes"] = ("nameWithoutLanguage", attrib) 626 return req.doRequest() # TODO : get the PPD from the actual print server 627 628 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 629 758 if __name__ == "__main__" : 630 759 if (len(sys.argv) < 2) or (sys.argv[1] == "--debug") : 631 print "usage : python ipp.py /var/spool/cups/c00005 [--debug] (for example)\n"760 print "usage : python pkipplib.py /var/spool/cups/c00005 [--debug] (for example)\n" 632 761 else : 633 762 infile = open(sys.argv[1], "rb") 634 data = infile.read()763 filedata = infile.read() 635 764 infile.close() 636 765 637 m essage = IPPRequest(data, debug=(sys.argv[-1]=="--debug"))638 m essage.parse()639 m essage2 = IPPRequest(message.dump())640 m essage2.parse()641 data2 = message2.dump()642 643 if data ==data2 :766 msg = IPPRequest(filedata, debug=(sys.argv[-1]=="--debug")) 767 msg.parse() 768 msg2 = IPPRequest(msg.dump()) 769 msg2.parse() 770 filedata2 = msg2.dump() 771 772 if filedata == filedata2 : 644 773 print "Test OK : parsing original and parsing the output of the dump produce the same dump !" 645 print str(m essage)774 print str(msg) 646 775 else : 647 776 print "Test Failed !" 648 print str(m essage)777 print str(msg) 649 778 print 650 print str(m essage2)651 779 print str(msg2) 780 -
/pkipplib/trunk/pkipplib/version.py
r14 r27 21 21 # 22 22 23 __version__ = "0.0 3"23 __version__ = "0.07" 24 24 25 25 __doc__ = "pkipplib : IPP and CUPS support for Python." -
/pkipplib/trunk/README
r19 r25 109 109 from pkipplib import pkipplib 110 110 111 # By default, connects to http://localhost:631 111 # Create a CUPS client instance 112 # cups = pkipplib.CUPS(url="http://server:631", username="john", password="blah!") 112 113 cups = pkipplib.CUPS() 113 114 … … 129 130 130 131 # Sends this request to the CUPS server 131 answer = request.doRequest()132 answer = cups.doRequest(request) 132 133 133 134 # Print the answer as a string of text