Changes in / [20:30]

Show
Ignore:
Location:
/pkipplib/trunk
Files:
2 added
4 modified

Legend:

Unmodified
Added
Removed
  • /pkipplib/trunk/tests/test.py

    r13 r28  
    2626import pkipplib         
    2727     
    28 cups = pkipplib.CUPS()     
    29 answer = cups.getPPD("HL6050DN") 
    30 #answer = cups.getJobAttributes(566) 
     28cups = pkipplib.CUPS() 
     29answer = cups.createSubscription("ipp://localhost/", ["printer-added", "printer-deleted"], 
     30                                                     userdata="samplenotifier:blah", 
     31                                                     recipient="samplenotifier", 
     32                                                     charset="utf-8") 
    3133print answer 
     34 
    3235#print answer.operation["attributes-charset"] 
    3336 
  • /pkipplib/trunk/pkipplib/pkipplib.py

    r18 r30  
    2424 
    2525import sys 
     26import os 
    2627import urllib2 
     28import socket 
    2729from struct import pack, unpack 
    2830 
     
    236238IPP_PRINTER_IS_DEACTIVATED = 0x50a 
    237239   
     240CUPS_PRINTER_LOCAL = 0x0000 
     241CUPS_PRINTER_CLASS = 0x0001 
     242CUPS_PRINTER_REMOTE = 0x0002 
     243CUPS_PRINTER_BW = 0x0004 
     244CUPS_PRINTER_COLOR = 0x0008 
     245CUPS_PRINTER_DUPLEX = 0x0010 
     246CUPS_PRINTER_STAPLE = 0x0020 
     247CUPS_PRINTER_COPIES = 0x0040 
     248CUPS_PRINTER_COLLATE = 0x0080 
     249CUPS_PRINTER_PUNCH = 0x0100 
     250CUPS_PRINTER_COVER = 0x0200 
     251CUPS_PRINTER_BIND = 0x0400 
     252CUPS_PRINTER_SORT = 0x0800 
     253CUPS_PRINTER_SMALL = 0x1000 
     254CUPS_PRINTER_MEDIUM = 0x2000 
     255CUPS_PRINTER_LARGE = 0x4000 
     256CUPS_PRINTER_VARIABLE = 0x8000 
     257CUPS_PRINTER_IMPLICIT = 0x1000 
     258CUPS_PRINTER_DEFAULT = 0x2000 
     259CUPS_PRINTER_FAX = 0x4000 
     260CUPS_PRINTER_REJECTING = 0x8000 
     261CUPS_PRINTER_DELETE = 0x1000 
     262CUPS_PRINTER_NOT_SHARED = 0x2000 
     263CUPS_PRINTER_AUTHENTICATED = 0x4000 
     264CUPS_PRINTER_COMMANDS = 0x8000 
     265CUPS_PRINTER_OPTIONS = 0xe6ff 
     266   
     267   
    238268class IPPError(Exception) : 
    239269    """An exception for IPP related stuff.""" 
     
    266296    def __getitem__(self, key) : 
    267297        """Returns an attribute's value.""" 
     298        answer = [] 
    268299        attributeslist = getattr(self.request, "_%s_attributes" % self.name) 
    269300        for i in range(len(attributeslist)) : 
     
    272303                (attrname, attrvalue) = attribute[j] 
    273304                if attrname == key : 
    274                     return attrvalue 
     305                    answer.extend(attrvalue) 
     306        if answer : 
     307            return answer 
    275308        raise KeyError, key             
    276309     
     
    282315                                operation_id=None, \ 
    283316                                request_id=None, \ 
    284                                 url = "http://localhost:631", \ 
    285                                 username = None, \ 
    286                                 password = None, \ 
    287317                                debug=False) : 
    288318        """Initializes an IPP Message object. 
     
    293323             debug : a boolean value to output debug info on stderr. 
    294324        """ 
    295         self.url = url 
    296         self.username = username 
    297         self.password = password 
    298325        self.debug = debug 
    299326        self._data = data 
     
    319346        self.tags[0x05] = "unsupported-attributes-tag" 
    320347        self.tags[0x06] = "subscription-attributes-tag" 
    321         self.tags[0x07] = "event-notification-attributes-tag" 
     348        self.tags[0x07] = "event_notification-attributes-tag" 
    322349         
    323350        # out of band values 
     
    418445        self.request_id = reqid 
    419446         
    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              
    428447    def dump(self) :     
    429448        """Generates an IPP Message. 
     
    433452        mybuffer = [] 
    434453        if None not in (self.version, self.operation_id) : 
    435             if self.request_id is None : 
    436                 self.nextRequestId() 
    437454            mybuffer.append(chr(self.version[0]) + chr(self.version[1])) 
    438455            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)) 
    440457            for attrtype in self.attributes_types : 
    441458                for attribute in getattr(self, "_%s_attributes" % attrtype) : 
     
    478495        self.position = 8 
    479496        endofattributes = self.tagvalues["end-of-attributes-tag"] 
    480         maxdelimiter = self.tagvalues["event-notification-attributes-tag"] 
     497        maxdelimiter = self.tagvalues["event_notification-attributes-tag"] 
    481498        nulloffset = lambda : 0 
    482499        try : 
     
    562579        return self.parseTag() 
    563580         
    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          
    582581             
    583582class CUPS : 
    584583    """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) : 
    586585        """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() 
    588592        self.username = username 
    589593        self.password = password 
    590594        self.charset = charset 
    591595        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             
    593613    def identifierToURI(self, service, ident) : 
    594614        """Transforms an identifier into a particular URI depending on requested service.""" 
     
    597617                             ident) 
    598618         
     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             
    599627    def newRequest(self, operationid=None) : 
    600628        """Generates a new empty request.""" 
    601629        if operationid is not None : 
    602630            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) 
    606633            req.operation["attributes-charset"] = ("charset", self.charset) 
    607634            req.operation["attributes-natural-language"] = ("naturalLanguage", self.language) 
    608635            return req 
    609636     
    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 
    613666     
    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          
    620667    def getPPD(self, queuename) :     
    621668        """Retrieves the PPD for a particular queuename.""" 
     
    624671        for attrib in ("printer-uri-supported", "printer-type", "member-uris") : 
    625672            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         
    629758if __name__ == "__main__" :             
    630759    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" 
    632761    else :     
    633762        infile = open(sys.argv[1], "rb") 
    634         data = infile.read() 
     763        filedata = infile.read() 
    635764        infile.close() 
    636765         
    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 : 
     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 : 
    644773            print "Test OK : parsing original and parsing the output of the dump produce the same dump !" 
    645             print str(message) 
     774            print str(msg) 
    646775        else :     
    647776            print "Test Failed !" 
    648             print str(message) 
     777            print str(msg) 
    649778            print 
    650             print str(message2) 
    651          
     779            print str(msg2) 
     780         
  • /pkipplib/trunk/pkipplib/version.py

    r14 r27  
    2121# 
    2222 
    23 __version__ = "0.03" 
     23__version__ = "0.07" 
    2424 
    2525__doc__ = "pkipplib : IPP and CUPS support for Python." 
  • /pkipplib/trunk/README

    r19 r25  
    109109from pkipplib import pkipplib 
    110110 
    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!") 
    112113cups = pkipplib.CUPS() 
    113114 
     
    129130     
    130131# Sends this request to the CUPS server     
    131 answer = request.doRequest()     
     132answer = cups.doRequest(request) 
    132133 
    133134# Print the answer as a string of text