Index: server/lib/ipplib.py
===================================================================
--- server/lib/ipplib.py	(revision d56a0bc5dc7dc8786c30fb5dfefec198f02e53df)
+++ server/lib/ipplib.py	(revision d56a0bc5dc7dc8786c30fb5dfefec198f02e53df)
@@ -0,0 +1,579 @@
+#! /usr/bin/env python
+import sys,os,urllib2,socket
+from struct import pack, unpack
+
+IPP_VERSION = "1.1"
+
+IPP_PORT = 631
+
+IPP_MAX_NAME = 256
+IPP_MAX_VALUES = 8
+
+IPP_TAG_ZERO = 0x00
+IPP_TAG_OPERATION = 0x01
+IPP_TAG_JOB = 0x02
+IPP_TAG_END = 0x03
+IPP_TAG_PRINTER = 0x04
+IPP_TAG_UNSUPPORTED_GROUP = 0x05
+IPP_TAG_SUBSCRIPTION = 0x06
+IPP_TAG_EVENT_NOTIFICATION = 0x07
+IPP_TAG_UNSUPPORTED_VALUE = 0x10
+IPP_TAG_DEFAULT = 0x11
+IPP_TAG_UNKNOWN = 0x12
+IPP_TAG_NOVALUE = 0x13
+IPP_TAG_NOTSETTABLE = 0x15
+IPP_TAG_DELETEATTR = 0x16
+IPP_TAG_ADMINDEFINE = 0x17
+IPP_TAG_INTEGER = 0x21
+IPP_TAG_BOOLEAN = 0x22
+IPP_TAG_ENUM = 0x23
+IPP_TAG_STRING = 0x30
+IPP_TAG_DATE = 0x31
+IPP_TAG_RESOLUTION = 0x32
+IPP_TAG_RANGE = 0x33
+IPP_TAG_BEGIN_COLLECTION = 0x34
+IPP_TAG_TEXTLANG = 0x35
+IPP_TAG_NAMELANG = 0x36
+IPP_TAG_END_COLLECTION = 0x37
+IPP_TAG_TEXT = 0x41
+IPP_TAG_NAME = 0x42
+IPP_TAG_KEYWORD = 0x44
+IPP_TAG_URI = 0x45
+IPP_TAG_URISCHEME = 0x46
+IPP_TAG_CHARSET = 0x47
+IPP_TAG_LANGUAGE = 0x48
+IPP_TAG_MIMETYPE = 0x49
+IPP_TAG_MEMBERNAME = 0x4a
+IPP_TAG_MASK = 0x7fffffff
+IPP_TAG_COPY = -0x7fffffff-1
+
+IPP_RES_PER_INCH = 3
+IPP_RES_PER_CM = 4
+
+IPP_FINISHINGS_NONE = 3
+IPP_FINISHINGS_STAPLE = 4
+IPP_FINISHINGS_PUNCH = 5
+IPP_FINISHINGS_COVER = 6
+IPP_FINISHINGS_BIND = 7
+IPP_FINISHINGS_SADDLE_STITCH = 8
+IPP_FINISHINGS_EDGE_STITCH = 9
+IPP_FINISHINGS_FOLD = 10
+IPP_FINISHINGS_TRIM = 11
+IPP_FINISHINGS_BALE = 12
+IPP_FINISHINGS_BOOKLET_MAKER = 13
+IPP_FINISHINGS_JOB_OFFSET = 14
+IPP_FINISHINGS_STAPLE_TOP_LEFT = 20
+IPP_FINISHINGS_STAPLE_BOTTOM_LEFT = 21
+IPP_FINISHINGS_STAPLE_TOP_RIGHT = 22
+IPP_FINISHINGS_STAPLE_BOTTOM_RIGHT = 23
+IPP_FINISHINGS_EDGE_STITCH_LEFT = 24
+IPP_FINISHINGS_EDGE_STITCH_TOP = 25
+IPP_FINISHINGS_EDGE_STITCH_RIGHT = 26
+IPP_FINISHINGS_EDGE_STITCH_BOTTOM = 27
+IPP_FINISHINGS_STAPLE_DUAL_LEFT = 28
+IPP_FINISHINGS_STAPLE_DUAL_TOP = 29
+IPP_FINISHINGS_STAPLE_DUAL_RIGHT = 30
+IPP_FINISHINGS_STAPLE_DUAL_BOTTOM = 31
+IPP_FINISHINGS_BIND_LEFT = 50
+IPP_FINISHINGS_BIND_TOP = 51
+IPP_FINISHINGS_BIND_RIGHT = 52
+IPP_FINISHINGS_BIND_BOTTO = 53
+
+IPP_PORTRAIT = 3
+IPP_LANDSCAPE = 4
+IPP_REVERSE_LANDSCAPE = 5
+IPP_REVERSE_PORTRAIT = 6
+
+IPP_QUALITY_DRAFT = 3
+IPP_QUALITY_NORMAL = 4
+IPP_QUALITY_HIGH = 5
+
+IPP_JOB_PENDING = 3
+IPP_JOB_HELD = 4
+IPP_JOB_PROCESSING = 5
+IPP_JOB_STOPPED = 6
+IPP_JOB_CANCELLED = 7
+IPP_JOB_ABORTED = 8
+IPP_JOB_COMPLETE = 9
+
+IPP_PRINTER_IDLE = 3
+IPP_PRINTER_PROCESSING = 4
+IPP_PRINTER_STOPPED = 5
+
+IPP_ERROR = -1
+IPP_IDLE = 0
+IPP_HEADER = 1
+IPP_ATTRIBUTE = 2
+IPP_DATA = 3
+
+IPP_PRINT_JOB = 0x0002
+IPP_PRINT_URI = 0x0003
+IPP_VALIDATE_JOB = 0x0004
+IPP_CREATE_JOB = 0x0005
+IPP_SEND_DOCUMENT = 0x0006
+IPP_SEND_URI = 0x0007
+IPP_CANCEL_JOB = 0x0008
+IPP_GET_JOB_ATTRIBUTES = 0x0009
+IPP_GET_JOBS = 0x000a
+IPP_GET_PRINTER_ATTRIBUTES = 0x000b
+IPP_HOLD_JOB = 0x000c
+IPP_RELEASE_JOB = 0x000d
+IPP_RESTART_JOB = 0x000e
+IPP_PAUSE_PRINTER = 0x0010
+IPP_RESUME_PRINTER = 0x0011
+IPP_PURGE_JOBS = 0x0012
+IPP_SET_PRINTER_ATTRIBUTES = 0x0013
+IPP_SET_JOB_ATTRIBUTES = 0x0014
+IPP_GET_PRINTER_SUPPORTED_VALUES = 0x0015
+IPP_CREATE_PRINTER_SUBSCRIPTION = 0x0016
+IPP_CREATE_JOB_SUBSCRIPTION = 0x0017
+IPP_GET_SUBSCRIPTION_ATTRIBUTES = 0x0018
+IPP_GET_SUBSCRIPTIONS = 0x0019
+IPP_RENEW_SUBSCRIPTION = 0x001a
+IPP_CANCEL_SUBSCRIPTION = 0x001b
+IPP_GET_NOTIFICATIONS = 0x001c
+IPP_SEND_NOTIFICATIONS = 0x001d
+IPP_GET_PRINT_SUPPORT_FILES = 0x0021
+IPP_ENABLE_PRINTER = 0x0022
+IPP_DISABLE_PRINTER = 0x0023
+IPP_PAUSE_PRINTER_AFTER_CURRENT_JOB = 0x0024
+IPP_HOLD_NEW_JOBS = 0x0025
+IPP_RELEASE_HELD_NEW_JOBS = 0x0026
+IPP_DEACTIVATE_PRINTER = 0x0027
+IPP_ACTIVATE_PRINTER = 0x0028
+IPP_RESTART_PRINTER = 0x0029
+IPP_SHUTDOWN_PRINTER = 0x002a
+IPP_STARTUP_PRINTER = 0x002b
+IPP_REPROCESS_JOB = 0x002c
+IPP_CANCEL_CURRENT_JOB = 0x002d
+IPP_SUSPEND_CURRENT_JOB = 0x002e
+IPP_RESUME_JOB = 0x002f
+IPP_PROMOTE_JOB = 0x0030
+IPP_SCHEDULE_JOB_AFTER = 0x0031
+IPP_PRIVATE = 0x4000
+CUPS_GET_DEFAULT = 0x4001
+CUPS_GET_PRINTERS = 0x4002
+CUPS_ADD_PRINTER = 0x4003
+CUPS_DELETE_PRINTER = 0x4004
+CUPS_GET_CLASSES = 0x4005
+CUPS_ADD_CLASS = 0x4006
+CUPS_DELETE_CLASS = 0x4007
+CUPS_ACCEPT_JOBS = 0x4008
+CUPS_REJECT_JOBS = 0x4009
+CUPS_SET_DEFAULT = 0x400a
+CUPS_GET_DEVICES = 0x400b
+CUPS_GET_PPDS = 0x400c
+CUPS_MOVE_JOB = 0x400d
+CUPS_AUTHENTICATE_JOB = 0x400e
+
+IPP_OK = 0x0000
+IPP_OK_SUBST = 0x0001
+IPP_OK_CONFLICT = 0x0002
+IPP_OK_IGNORED_SUBSCRIPTIONS = 0x0003
+IPP_OK_IGNORED_NOTIFICATIONS = 0x0004
+IPP_OK_TOO_MANY_EVENTS = 0x0005
+IPP_OK_BUT_CANCEL_SUBSCRIPTION = 0x0006
+IPP_REDIRECTION_OTHER_SITE = 0x0300
+IPP_BAD_REQUEST = 0x0400
+IPP_FORBIDDEN = 0x0401
+IPP_NOT_AUTHENTICATED = 0x0402
+IPP_NOT_AUTHORIZED = 0x0403
+IPP_NOT_POSSIBLE = 0x0404
+IPP_TIMEOUT = 0x0405
+IPP_NOT_FOUND = 0x0406
+IPP_GONE = 0x0407
+IPP_REQUEST_ENTITY = 0x0408
+IPP_REQUEST_VALUE = 0x0409
+IPP_DOCUMENT_FORMAT = 0x040a
+IPP_ATTRIBUTES = 0x040b
+IPP_URI_SCHEME = 0x040c
+IPP_CHARSET = 0x040d
+IPP_CONFLICT = 0x040e
+IPP_COMPRESSION_NOT_SUPPORTED = 0x040f
+IPP_COMPRESSION_ERROR = 0x0410
+IPP_DOCUMENT_FORMAT_ERROR = 0x0411
+IPP_DOCUMENT_ACCESS_ERROR = 0x0412
+IPP_ATTRIBUTES_NOT_SETTABLE = 0x0413
+IPP_IGNORED_ALL_SUBSCRIPTIONS = 0x0414
+IPP_TOO_MANY_SUBSCRIPTIONS = 0x0415
+IPP_IGNORED_ALL_NOTIFICATIONS = 0x0416
+IPP_PRINT_SUPPORT_FILE_NOT_FOUND = 0x0417
+
+IPP_INTERNAL_ERROR = 0x0500
+IPP_OPERATION_NOT_SUPPORTED = 0x0501
+IPP_SERVICE_UNAVAILABLE = 0x0502
+IPP_VERSION_NOT_SUPPORTED = 0x0503
+IPP_DEVICE_ERROR = 0x0504
+IPP_TEMPORARY_ERROR = 0x0505
+IPP_NOT_ACCEPTING = 0x0506
+IPP_PRINTER_BUSY = 0x0507
+IPP_ERROR_JOB_CANCELLED = 0x0508
+IPP_MULTIPLE_JOBS_NOT_SUPPORTED = 0x0509
+IPP_PRINTER_IS_DEACTIVATED = 0x50a
+  
+CUPS_PRINTER_LOCAL = 0x0000
+CUPS_PRINTER_CLASS = 0x0001
+CUPS_PRINTER_REMOTE = 0x0002
+CUPS_PRINTER_BW = 0x0004
+CUPS_PRINTER_COLOR = 0x0008
+CUPS_PRINTER_DUPLEX = 0x0010
+CUPS_PRINTER_STAPLE = 0x0020
+CUPS_PRINTER_COPIES = 0x0040
+CUPS_PRINTER_COLLATE = 0x0080
+CUPS_PRINTER_PUNCH = 0x0100
+CUPS_PRINTER_COVER = 0x0200
+CUPS_PRINTER_BIND = 0x0400
+CUPS_PRINTER_SORT = 0x0800
+CUPS_PRINTER_SMALL = 0x1000
+CUPS_PRINTER_MEDIUM = 0x2000
+CUPS_PRINTER_LARGE = 0x4000
+CUPS_PRINTER_VARIABLE = 0x8000
+CUPS_PRINTER_IMPLICIT = 0x10000
+CUPS_PRINTER_DEFAULT = 0x20000
+CUPS_PRINTER_FAX = 0x40000
+CUPS_PRINTER_REJECTING = 0x80000
+CUPS_PRINTER_DELETE = 0x100000
+CUPS_PRINTER_NOT_SHARED = 0x200000
+CUPS_PRINTER_AUTHENTICATED = 0x400000
+CUPS_PRINTER_COMMANDS = 0x800000
+CUPS_PRINTER_OPTIONS = 0xe6ff
+  
+  
+class IPPError(Exception) :
+    """An exception for IPP related stuff."""
+    def __init__(self, message = ""):
+        self.message = message
+        Exception.__init__(self, message)
+    def __repr__(self):
+        return self.message
+    __str__ = __repr__
+
+class FakeAttribute :
+    """Fakes an IPPRequest attribute to simplify usage syntax."""
+    def __init__(self, request, name) :
+        """Initializes the fake attribute."""
+        self.request = request
+        self.name = name
+        
+    def __setitem__(self, key, value) :
+        """Appends the value to the real attribute."""
+        attributeslist = getattr(self.request, "_%s_attributes" % self.name)
+        for i in range(len(attributeslist)) :
+            attribute = attributeslist[i]
+            for j in range(len(attribute)) :
+                (attrname, attrvalue) = attribute[j]
+                if attrname == key :
+                    attribute[j][1].append(value)
+                    return
+            attribute.append((key, [value]))
+            
+    def __getitem__(self, key) :
+        """Returns an attribute's value."""
+        answer = []
+        attributeslist = getattr(self.request, "_%s_attributes" % self.name)
+        for i in range(len(attributeslist)) :
+            attribute = attributeslist[i]
+            for j in range(len(attribute)) :
+                (attrname, attrvalue) = attribute[j]
+                if attrname == key :
+                    answer.extend(attrvalue)
+        if answer :
+            return answer
+        raise KeyError, key
+    
+class IPPRequest :
+    """A class for IPP requests."""
+    attributes_types = ("operation", "job", "printer", "unsupported", \
+                                     "subscription", "event_notification")
+    def __init__(self, data="", version=IPP_VERSION,
+                                operation_id=None, \
+                                request_id=None, \
+                                debug=False) :
+        """Initializes an IPP Message object.
+        
+           Parameters :
+           
+             data : the complete IPP Message's content.
+             debug : a boolean value to output debug info on stderr.
+        """
+        self.debug = debug
+        self._data = data
+        self.parsed = False
+        
+        # Initializes message
+        self.setVersion(version)
+        self.setOperationId(operation_id)
+        self.setRequestId(request_id)
+        self.data = ""
+        
+        for attrtype in self.attributes_types :
+            setattr(self, "_%s_attributes" % attrtype, [[]])
+        
+        # Initialize tags    
+        self.tags = [ None ] * 256 # by default all tags reserved
+        
+        # Delimiter tags
+        self.tags[0x01] = "operation-attributes-tag"
+        self.tags[0x02] = "job-attributes-tag"
+        self.tags[0x03] = "end-of-attributes-tag"
+        self.tags[0x04] = "printer-attributes-tag"
+        self.tags[0x05] = "unsupported-attributes-tag"
+        self.tags[0x06] = "subscription-attributes-tag"
+        self.tags[0x07] = "event_notification-attributes-tag"
+        
+        # out of band values
+        self.tags[0x10] = "unsupported"
+        self.tags[0x11] = "reserved-for-future-default"
+        self.tags[0x12] = "unknown"
+        self.tags[0x13] = "no-value"
+        self.tags[0x15] = "not-settable"
+        self.tags[0x16] = "delete-attribute"
+        self.tags[0x17] = "admin-define"
+  
+        # integer values
+        self.tags[0x20] = "generic-integer"
+        self.tags[0x21] = "integer"
+        self.tags[0x22] = "boolean"
+        self.tags[0x23] = "enum"
+        
+        # octetString
+        self.tags[0x30] = "octetString-with-an-unspecified-format"
+        self.tags[0x31] = "dateTime"
+        self.tags[0x32] = "resolution"
+        self.tags[0x33] = "rangeOfInteger"
+        self.tags[0x34] = "begCollection"
+        self.tags[0x35] = "textWithLanguage"
+        self.tags[0x36] = "nameWithLanguage"
+        self.tags[0x37] = "endCollection"
+        
+        # character strings
+        self.tags[0x40] = "generic-character-string"
+        self.tags[0x41] = "textWithoutLanguage"
+        self.tags[0x42] = "nameWithoutLanguage"
+        self.tags[0x44] = "keyword"
+        self.tags[0x45] = "uri"
+        self.tags[0x46] = "uriScheme"
+        self.tags[0x47] = "charset"
+        self.tags[0x48] = "naturalLanguage"
+        self.tags[0x49] = "mimeMediaType"
+        self.tags[0x4a] = "memberAttrName"
+        
+        # Reverse mapping to generate IPP messages
+        self.tagvalues = {}
+        for i in range(len(self.tags)) :
+            value = self.tags[i]
+            if value is not None :
+                self.tagvalues[value] = i
+                                     
+    def __getattr__(self, name) :
+        """Fakes attribute access."""
+        if name in self.attributes_types :
+            return FakeAttribute(self, name)
+        else :
+            raise AttributeError, name
+            
+    def __str__(self) :
+        """Returns the parsed IPP message in a readable form."""
+        if not self.parsed :
+            return ""
+        mybuffer = []
+        mybuffer.append("IPP version : %s.%s" % self.version)
+        mybuffer.append("IPP operation Id : 0x%04x" % self.operation_id)
+        mybuffer.append("IPP request Id : 0x%08x" % self.request_id)
+        for attrtype in self.attributes_types :
+            for attribute in getattr(self, "_%s_attributes" % attrtype) :
+                if attribute :
+                    mybuffer.append("%s attributes :" % attrtype.title())
+                for (name, value) in attribute :
+                    mybuffer.append("  %s : %s" % (name, value))
+        # if self.data :
+        #     mybuffer.append("IPP datas : %s" % repr(self.data))
+        return "\n".join(mybuffer)
+        
+    def logDebug(self, msg) :
+        """Prints a debug message."""
+        if self.debug :
+            sys.stderr.write("%s\n" % msg)
+            sys.stderr.flush()
+            
+    def setVersion(self, version) :
+        """Sets the request's operation id."""
+        if version is not None :
+            try :
+                self.version = [int(p) for p in version.split(".")]
+            except AttributeError :
+                if len(version) == 2 : # 2-tuple
+                    self.version = version
+                else :
+                    try :
+                        self.version = [int(p) for p in str(float(version)).split(".")]
+                    except :
+                        self.version = [int(p) for p in IPP_VERSION.split(".")]
+        
+    def setOperationId(self, opid) :
+        """Sets the request's operation id."""
+        self.operation_id = opid
+        
+    def setRequestId(self, reqid) :
+        """Sets the request's request id."""
+        self.request_id = reqid
+        
+    def dump(self) :
+        """Generates an IPP Message.
+        
+           Returns the message as a string of text.
+        """    
+        mybuffer = []
+        if None not in (self.version, self.operation_id) :
+            mybuffer.append(chr(self.version[0]) + chr(self.version[1]))
+            mybuffer.append(pack(">H", self.operation_id))
+            mybuffer.append(pack(">I", self.request_id or 1))
+            for attrtype in self.attributes_types :
+                for attribute in getattr(self, "_%s_attributes" % attrtype) :
+                    if attribute :
+                        mybuffer.append(chr(self.tagvalues["%s-attributes-tag" % attrtype]))
+                    for (attrname, value) in attribute :
+                        nameprinted = 0
+                        for (vtype, val) in value :
+                            mybuffer.append(chr(self.tagvalues[vtype]))
+                            if not nameprinted :
+                                mybuffer.append(pack(">H", len(attrname)))
+                                mybuffer.append(attrname)
+                                nameprinted = 1
+                            else :
+                                mybuffer.append(pack(">H", 0))
+                            if vtype in ("integer", "enum") :
+                                mybuffer.append(pack(">H", 4))
+                                mybuffer.append(pack(">I", val))
+                            elif vtype == "boolean" :
+                                mybuffer.append(pack(">H", 1))
+                                mybuffer.append(chr(val))
+                            else :
+                                mybuffer.append(pack(">H", len(val)))
+                                mybuffer.append(val)
+            mybuffer.append(chr(self.tagvalues["end-of-attributes-tag"]))
+        mybuffer.append(self.data)
+        return "".join(mybuffer)
+
+    def parse(self) :
+        """Parses an IPP Request.
+        
+           NB : Only a subset of RFC2910 is implemented.
+        """
+        self._curname = None
+        self._curattributes = None
+        
+        self.setVersion((ord(self._data[0]), ord(self._data[1])))
+        self.setOperationId(unpack(">H", self._data[2:4])[0])
+        self.setRequestId(unpack(">I", self._data[4:8])[0])
+        self.position = 8
+        endofattributes = self.tagvalues["end-of-attributes-tag"]
+        maxdelimiter = self.tagvalues["event_notification-attributes-tag"]
+        nulloffset = lambda : 0
+        #try :
+        if 1:
+            tag = ord(self._data[self.position])
+            while tag != endofattributes :
+                self.position += 1
+                name = self.tags[tag]
+                if name is not None :
+                    func = getattr(self, name.replace("-", "_"), nulloffset)
+                    self.position += func()
+                    if ord(self._data[self.position]) > maxdelimiter :
+                        self.position -= 1
+                        continue
+                oldtag = tag
+                tag = ord(self._data[self.position])
+                if tag == oldtag :
+                    self._curattributes.append([])
+        #except IndexError :
+        #    raise IPPError, "Unexpected end of IPP message."
+            
+        self.data = self._data[self.position+1:]
+        self.parsed = True
+        
+    def parseTag(self) :
+        """Extracts information from an IPP tag."""
+        pos = self.position
+        tagtype = self.tags[ord(self._data[pos])]
+        if tagtype == 'end-of-attributes-tag':
+            return 0
+        pos += 1
+        posend = pos2 = pos + 2
+        namelength = unpack(">H", self._data[pos:pos2])[0]
+        if not namelength :
+            name = self._curname
+        else :
+            posend += namelength
+            self._curname = name = self._data[pos2:posend]
+        pos2 = posend + 2
+        valuelength = unpack(">H", self._data[posend:pos2])[0]
+        posend = pos2 + valuelength
+        value = self._data[pos2:posend]
+        if tagtype in ("integer", "enum") :
+            value = unpack(">I", value)[0]
+        elif tagtype == "boolean" :
+            value = ord(value)
+        try :
+            (oldname, oldval) = self._curattributes[-1][-1]
+            if oldname == name :
+                oldval.append((tagtype, value))
+            else :
+                raise IndexError
+        except IndexError :
+            self._curattributes[-1].append((name, [(tagtype, value)]))
+        self.logDebug("%s(%s) : %s" % (name, tagtype, value))
+        return posend - self.position
+        
+    def operation_attributes_tag(self) :
+        """Indicates that the parser enters into an operation-attributes-tag group."""
+        self._curattributes = self._operation_attributes
+        return self.parseTag()
+        
+    def job_attributes_tag(self) :
+        """Indicates that the parser enters into a job-attributes-tag group."""
+        self._curattributes = self._job_attributes
+        return self.parseTag()
+        
+    def printer_attributes_tag(self) :
+        """Indicates that the parser enters into a printer-attributes-tag group."""
+        self._curattributes = self._printer_attributes
+        return self.parseTag()
+        
+    def unsupported_attributes_tag(self) :
+        """Indicates that the parser enters into an unsupported-attributes-tag group."""
+        self._curattributes = self._unsupported_attributes
+        return self.parseTag()
+        
+    def subscription_attributes_tag(self) :
+        """Indicates that the parser enters into a subscription-attributes-tag group."""
+        self._curattributes = self._subscription_attributes
+        return self.parseTag()
+        
+    def event_notification_attributes_tag(self) :
+        """Indicates that the parser enters into an event-notification-attributes-tag group."""
+        self._curattributes = self._event_notification_attributes
+        return self.parseTag()
+
+if __name__ == "__main__" :
+    if (len(sys.argv) < 2) or (sys.argv[1] == "--debug") :
+        print "usage : python pkipplib.py /var/spool/cups/c00005 [--debug] (for example)\n"
+    else :
+        infile = open(sys.argv[1], "rb")
+        filedata = infile.read()
+        infile.close()
+        
+        msg = IPPRequest(filedata, debug=(sys.argv[-1]=="--debug"))
+        msg.parse()
+        msg2 = IPPRequest(msg.dump())
+        msg2.parse()
+        filedata2 = msg2.dump()
+        
+        if filedata == filedata2 :
+            print "Test OK : parsing original and parsing the output of the dump produce the same dump !"
+            print str(msg)
+        else :
+            print "Test Failed !"
+            print str(msg)
+            print
+            print str(msg2)
Index: server/lib/ipprequest.py
===================================================================
--- server/lib/ipprequest.py	(revision 4ec7caa5478479c329bfc1eeff8ac44e24631060)
+++ server/lib/ipprequest.py	(revision d56a0bc5dc7dc8786c30fb5dfefec198f02e53df)
@@ -10,46 +10,59 @@
     
     # various tags
-    ZERO_NAME_LENGTH                  = 0X00
-    OPERATION_ATTRIBUTES_TAG          = 0X01
-    JOB_ATTRIBUTES_TAG                = 0X02
-    END_OF_ATTRIBUTES_TAG             = 0X03
-    PRINTER_ATTRIBUTES_TAG            = 0X04
-    UNSUPPORTED_ATTRIBUTES_TAG        = 0X05
+    ZERO_NAME_LENGTH                  = 0x00
+    OPERATION_ATTRIBUTES_TAG          = 0x01
+    JOB_ATTRIBUTES_TAG                = 0x02
+    END_OF_ATTRIBUTES_TAG             = 0x03
+    PRINTER_ATTRIBUTES_TAG            = 0x04
+    UNSUPPORTED_ATTRIBUTES_TAG        = 0x05
     
     # "out of band" value tags
-    UNSUPPORTED                       = 0X10
-    DEFAULT                           = 0X11
-    UNKNOWN                           = 0X12
-    NO_VALUE                          = 0X13
+    UNSUPPORTED                       = 0x10
+    DEFAULT                           = 0x11
+    UNKNOWN                           = 0x12
+    NO_VALUE                          = 0x13
     
     # integer value tags
-    GENERIC_INTEGER                   = 0X20
-    INTEGER                           = 0X21
-    BOOLEAN                           = 0X22
-    ENUM                              = 0X23
+    GENERIC_INTEGER                   = 0x20
+    INTEGER                           = 0x21
+    BOOLEAN                           = 0x22
+    ENUM                              = 0x23
 
     # octetstring value tags
-    UNSPECIFIED_OCTETSTRING           = 0X30
-    DATETIME                          = 0X31
-    RESOLUTION                        = 0X32
-    RANGEOFINTEGER                    = 0X33
-    COLLECTION                        = 0X34
-    TEXTWITHLANGUAGE                  = 0X35
-    NAMEWITHLANGUAGE                  = 0X36
+    UNSPECIFIED_OCTETSTRING           = 0x30
+    DATETIME                          = 0x31
+    RESOLUTION                        = 0x32
+    RANGEOFINTEGER                    = 0x33
+    COLLECTION                        = 0x34
+    TEXTWITHLANGUAGE                  = 0x35
+    NAMEWITHLANGUAGE                  = 0x36
 
     # character-string value tags
-    GENERIC_CHAR_STRING               = 0X40
-    TEXTWITHOUTLANGUAGE               = 0X41
-    NAMEWITHOUTLANGUAGE               = 0X42
-    KEYWORD                           = 0X44
-    URI                               = 0X45
-    URISCHEME                         = 0X46
-    CHARSET                           = 0X47
-    NATURALLANGUAGE                   = 0X48
-    MIMEMEDIATYPE                     = 0X49                                    
+    GENERIC_CHAR_STRING               = 0x40
+    TEXTWITHOUTLANGUAGE               = 0x41
+    NAMEWITHOUTLANGUAGE               = 0x42
+    KEYWORD                           = 0x44
+    URI                               = 0x45
+    URISCHEME                         = 0x46
+    CHARSET                           = 0x47
+    NATURALLANGUAGE                   = 0x48
+    MIMEMEDIATYPE                     = 0x49                                    
 
 class IPPValue():
     """
     An IPP value consists of a tag and a value, and optionally, a name.
+
+    From RFC 2565:
+     -----------------------------------------------
+     |                   value-tag                 |   1 byte
+     -----------------------------------------------
+     |               name-length  (value is u)     |   2 bytes
+     -----------------------------------------------
+     |                     name                    |   u bytes
+     -----------------------------------------------
+     |              value-length  (value is v)     |   2 bytes
+     -----------------------------------------------
+     |                     value                   |   v bytes
+     -----------------------------------------------    
     """
 
@@ -78,4 +91,32 @@
         self.name = str(name)
         self.value = str(value)
+
+    def toBinaryData(self):
+        """
+        Packs the value data into binary data.
+        """
+        
+        # get the length in bytes of the name
+        name_length = sys.getsizeof(name)
+        # get the length in bytes of the value
+        value_length = sys.getsizeof(value)
+
+        # if the name is of length zero, then don't include it in the
+        # binary data
+        if name_length == IPPTags.ZERO_NAME_LENGTH:
+            binary = struct.pack('>bhhs',
+                                 self.value_tag,
+                                 name_length,
+                                 value_length,
+                                 self.value)
+        else:
+            binary = struct.pack('>bhshs',
+                                 self.value_tag,
+                                 name_length,
+                                 self.name,
+                                 value_length,
+                                 self.value)
+
+        return binary
 
 class IPPAttribute():
@@ -135,4 +176,17 @@
          self.attribute_tag = hex(attribute_tag)
          self.values = values
+
+    def toBinaryData(self):
+        """
+        Packs the attribute data into binary data.
+        """
+
+        # pack the attribute tag
+        attribute_tag = struct.pack('>b', self.attribute_tag)
+        # get the binary data for all the values
+        values = [v.toBinaryData() for v in self.values]
+
+        # concatenate everything together and return it
+        return attribute_tag + ''.join(values)
 
 class IPPRequest():
@@ -216,12 +270,12 @@
         if request is not None:
             # read the version-number (two signed chars)
-            self.version        = struct.unpack('bb', request.read(2))
+            self.version        = struct.unpack('>bb', request.read(2))
 
             # read the operation-id (or status-code, but that's only
             # for a response) (signed short)
-            self.operation_id   = struct.unpack('h', request.read(2))
+            self.operation_id   = struct.unpack('>h', request.read(2))
 
             # read the request-id (signed int)
-            self.request_id     = struct.unpack('i', request.read(4))
+            self.request_id     = struct.unpack('>i', request.read(4))
 
             # now we have to read in the attributes.  Each attribute
@@ -230,5 +284,5 @@
 
             # read in the next byte
-            next_byte = struct.unpack('b', request.read(1))
+            next_byte = struct.unpack('>b', request.read(1))
 
             # as long as the next byte isn't signaling the end of the
@@ -242,13 +296,13 @@
                     attribute_tag = next_byte
                     # read in the value tag (signed char)
-                    value_tag     = struct.unpack('b', request.read(1))
+                    value_tag     = struct.unpack('>b', request.read(1))
                     # read in the length of the name (signed short)
-                    name_length   = struct.unpack('h', request.read(2))
+                    name_length   = struct.unpack('>h', request.read(2))
                     # read the name (a string of name_length bytes)
-                    name          = struct.unpack('s', request.read(name_length))
+                    name          = struct.unpack('>s', request.read(name_length))
                     # read in the length of the value (signed short)
-                    value_length  = struct.unpack('h', request.read(2))
+                    value_length  = struct.unpack('>h', request.read(2))
                     # read in the value (string of value_length bytes)
-                    value         = struct.unpack('b'*value_length, request.read(value_length))
+                    value         = struct.unpack('>%sb' % value_length, request.read(value_length))
 
                     # create a new IPPAttribute from the data we just
@@ -265,12 +319,12 @@
                     # read in the length of the name (two bytes) --
                     # this should be 0x0
-                    name_length   = struct.unpack('h', request.read(2))
-                    assert name_length == zero_name_length
+                    name_length   = struct.unpack('>h', request.read(2))
+                    assert name_length == IPPTags.ZERO_NAME_LENGTH
                     # name should be empty
                     name          = ''
                     # read in the length of the value (two bytes)
-                    value_length  = struct.unpack('h', request.read(2))
+                    value_length  = struct.unpack('>h', request.read(2))
                     # read in the value (value_length bytes)
-                    value         = struct.unpack('b'*value_length, request.read(value_length))
+                    value         = struct.unpack('>%sb' % value_length, request.read(value_length))
 
                     # add another value to the last attribute
@@ -278,10 +332,10 @@
 
                 # read another byte
-                next_byte = struct.unpack('b', request.read(1))
+                next_byte = struct.unpack('>b', request.read(1))
 
             # once we hit the end-of-attributes tag, the only thing
             # left is the data, so go ahead and read all of it
             buff = request.read()
-            self.data = struct.unpack('b'*len(buff), sys.getsizeof(buff))
+            self.data = struct.unpack('>%sb' % len(buff), sys.getsizeof(buff))
 
         # otherwise, just set the class variables to the keyword
@@ -293,2 +347,26 @@
             self.attributes = attributes
             self.data = data
+
+    def toBinaryData(self):
+        """
+        Packs the value data into binary data.
+        """
+
+        # convert the version, operation id, and request id to binary
+        preattributes = struct.pack('>bbhi',
+                                    self.version[0],
+                                    self.version[1],
+                                    self.operation_id,
+                                    self.request_id)
+
+        # convert the attributes to binary
+        attributes = ''.join([a.toBinaryData() for a in attributes])
+
+        # conver the end-of-attributes-tag to binary
+        end_of_attributes_tag = struct.pack('>b', IPPTags.END_OF_ATTRIBUTES_TAG)
+
+        # convert the data to binary
+        data = ''.join([struct.pack('>b', x) for x in self.data])
+
+        # append everything together and return it
+        return preattributes + attributes + end_of_attributes_tag + data
