Index: server/lib/ippattribute.py
===================================================================
--- server/lib/ippattribute.py	(revision ebf327dd25c74049a9010d04d52f522d8598cd18)
+++ server/lib/ippattribute.py	(revision c269bc7fb4d107878536921b17ef5bd63edabf68)
@@ -40,7 +40,15 @@
     """
 
-    def __init__(self, name, values):
-        """
-        Initialize an Attribute.
+    def __init__(self, name=None, values=[]):
+        """
+        Initialize an Attribute.  This function can be called in three
+        different ways:
+
+            Attribute() -- creates an empty Attribute
+
+            Attribute(name) -- creates an empty Attribute with a name
+
+            Attribute(name, values) -- creates an Attribute
+            initialized with a name and list of values
         
         Arguments:
@@ -51,22 +59,34 @@
         """
 
-        # make sure name isn't empty
-        assert name is not None
-         
-        # make sure the list of values isn't empty
-        assert len(values) > 0
-        # make sure each value is a Value
-        for value in values: assert isinstance(value, Value)
-         
-        self.name = name
-        self.values = values
-
-    def toBinaryData(self):
+        if name is not None:
+            assert isinstance(name, str), \
+                   "Attribute name must be a string!"
+        for value in values:
+            assert isinstance(value, Value), \
+                   "Value must be of type Value"
+
+        self.name = None
+        self.values = None
+
+        if name is not None:
+            self.name = name
+        if name is not None and len(values) > 0:
+            self.values = values
+            self.binary = self.pack()
+            self.verify()
+
+    def pack(self):
         """
         Packs the attribute data into binary data.
         """
+
+        assert self.name is not None, \
+               "cannot pack unnamed attribute!"
+        assert len(self.values) > 0, \
+               "cannot pack empty attribute!"
 
         # get the binary data for all the values
         values = []
+        length = 0
         for v, i in zip(self.values, xrange(len(self.values))):
 
@@ -79,13 +99,13 @@
 
             # get the value length and binary value
-            value_length, value_bin = v.valueToBinary()
+            value_length, value_bin = v.pack()
 
             logger.debug("dumping name_length : %i" % name_length)
             logger.debug("dumping name : %s" % self.name)
             logger.debug("dumping value_length : %i" % value_length)
-            logger.debug("dumping value : %s" % v.value)
+            logger.debug("dumping value : %s" % v.getValue())
 
             # the value tag in binary
-            value_tag_bin = struct.pack('>b', v.value_tag)
+            value_tag_bin = struct.pack('>b', v.getValueTag())
 
             # the name length in binary
@@ -109,5 +129,97 @@
                                        value_length_bin,
                                        value_bin]))
+
+            length += 2            # name-length
+            length += name_length  # name
+            length += value_length # value
                 
-        # concatenate everything together and return it
-        return ''.join(values)
+        # concatenate everything together and return it along with the
+        # total length of the attribute
+        return length, ''.join(values)
+
+    def getName(self):
+        """
+        Get the attribute's name.
+        """
+        
+        return self.name
+
+    def getValues(self):
+        """
+        Get the list of values contained in the attribute.
+        """
+        
+        return self.values
+
+    def setName(self, name):
+        """
+        Set the attribute's name.
+        """
+
+        assert isinstance(name, str), \
+               "name must be a string!"
+        
+        self.name = name
+
+    def setValues(self, values):
+        """
+        Set the list of values contained in the attribute.
+        """
+
+        for v in values:
+            assert isinstance(v, Value), \
+                   "value must be of type Value!"
+        
+        self.values = values
+
+    def addValue(self, value):
+        """
+        Add a new value to the list of values contained in the
+        attribute."
+
+        assert isinstance(value, Value), \
+               "value must be of type Value!"
+
+        self.values.append(value)
+
+    def getSize(self):
+        """
+        Gets the total size of the attribute.
+        """
+
+        size = 2 + len(self.name)
+        for v in self.values:
+            size += v.getTotalSize()
+
+    def verify(self):
+        """
+        Check to make sure that the Attribute is valid.  This means
+        that is should have a name and a list of values, and that its
+        binary representation should match up to the stored binary
+        representation.
+        """
+        
+        assert self.name is not None, \
+               "attribute has no name!"
+        assert len(self.value) > 0, \
+               "attribute has no values!"
+
+        size, bindata = self.pack()
+
+        assert size == self.getSize(), \
+               "pack size does not match self.getSize()!"
+        assert bindata == self.binary_data, \
+               "packed binary data does not match self.binary_data!"
+
+    def __str__(self):
+        if len(self.values) > 0:
+            values = [str(v) for v in self.values]
+        else:
+            values = "None"
+
+        if self.name is None:
+            name = "None"
+        else:
+            name = self.name
+        
+        return "%s: %s" % (name, str(values))
Index: server/lib/ippattributegroup.py
===================================================================
--- server/lib/ippattributegroup.py	(revision ebf327dd25c74049a9010d04d52f522d8598cd18)
+++ server/lib/ippattributegroup.py	(revision c269bc7fb4d107878536921b17ef5bd63edabf68)
@@ -13,7 +13,12 @@
     """
 
-    def __init__(self, attribute_group_tag, attributes=[]):
+    def __init__(self, attribute_group_tag=None, attributes=[]):
         """
-        Initialize an AttributeGroup.
+        Initialize an AttributeGroup.  An AttributeGroup can be
+        initialized in three ways:
+
+            AttributeGroup()
+            AttributeGroup(attribute_group_tag)
+            AttributeGroup(attribute_group_tag, attributes)
 
         Arguments:
@@ -22,13 +27,16 @@
                                    attribute group
 
-            attributes -- (optional) a list of attributes
+            attributes -- a list of attributes
         """
 
-        # make sure attribute_group_tag isn't empty
-        assert attribute_group_tag is not None
+        if attribute_group_tag is not None:
+            assert isinstance(attribute_group_tag, char), \
+                   "attribute_group_tag must be a character!"
+            
 
-        # make sure attributes is a list or tuple of Attributes
-        assert isinstance(attributes, (list, tuple))
-        for a in attributes: assert isinstance(a, Attribute)
+        if len(attributes) > 0:
+            for a in attributes:
+                assert isinstance(a, Attribute), \
+                       "attribute must be of type Attribute!"
 
         self.attribute_group_tag = attribute_group_tag
@@ -36,7 +44,57 @@
 
     def getAttribute(self, name):
+        """
+        Returns a list of attributes which have name 'name'.
+        """
+        
         return filter(lambda x: x.name == name, self.attributes)
 
-    def toBinaryData(self):
+    def getTag(self):
+        """
+        Return the attribute group tag.
+        """
+
+        return self.attribute_group_tag
+
+    def getAttributes(self):
+        """
+        Return the list of attributes in this attribue group.
+        """
+
+        return self.attributes
+
+    def setAttributes(self, attributes):
+        """
+        Sets the attributes for the attribute group.
+        """
+
+        for a in attributes:
+            assert isinstance(a, Attribute), \
+                   "attribute must be of type Attribute!"
+
+        self.attributes = attributes
+
+    def addAttribute(self, attribute):
+        """
+        Adds an attribute to the list of attributes for the attribute
+        group.
+        """
+        
+        assert isinstance(attribute, Attribute), \
+               "attribute must be of type Attribute!"
+
+        self.attributes.append(attribute)
+
+    def setTag(self, tag):
+        """
+        Sets the attribute group tag.
+        """
+
+        assert isinstance(tag, char), \
+               "attribute tag must be a character!"
+
+        self.attribute_group_tag = tag
+
+    def pack(self):
         """
         Convert the AttributeGroup to binary.
Index: server/lib/ippvalue.py
===================================================================
--- server/lib/ippvalue.py	(revision 5cfb3586e3e02ab00da4607080261d17b403c48d)
+++ server/lib/ippvalue.py	(revision c269bc7fb4d107878536921b17ef5bd63edabf68)
@@ -65,5 +65,6 @@
         self.value_tag    = None # one byte, the type of value
         self.value        = None # non-binary value of self.value
-        self.value_size   = None # size of self.value
+        self.tag_size     = 1    # length of the tag in bytes
+        self.value_size   = None # size of self.value in bytes
         self.binary_value = None # binary value of self.value
 
@@ -72,11 +73,11 @@
                 self.value_tag = value_tag
                 self.binary_value = value
-                self.unpack()
-                self.pack()
+                self.value_size, self.value = self.unpack()
             else:
                 self.value_tag = value_tag
                 self.value = value
-                self.pack()
-                self.unpack()
+                self.value_size, self.binary_value = self.pack()
+
+            self.verify()
 
     def unpack(self):
@@ -196,7 +197,4 @@
                    "unpacked value is not the same as self.value!"
 
-        self.value_size = value_size
-        self.value = value
-        
         return value_size, value
 
@@ -313,16 +311,4 @@
             binary_value = self.value
 
-        if self.value_size is not None:
-            assert value_size == self.value_size, \
-                   "packed value size is not the same as " + \
-                   "self.value_size!"
-        if self.binary_value is not None:
-            assert binary_value == self.binary_value, \
-                   "packed binary value is not the same as " + \
-                   "self.binary_value!"
-
-        self.value_size = value_size
-        self.binary_value = binary_value
-
         return value_size, binary_value
 
@@ -346,6 +332,16 @@
                "value type is unknown!"
 
-        self.pack()
-        self.unpack()
+        value_size1, binary_value = self.pack()
+        value_size2, value = self.unpack()
+
+        assert value_size1 == value_size2, \
+               "packed value size is not the same as " + \
+               "unpacked value size!"
+        assert value_size == self.value_size, \
+               "packed value size is not the same as " + \
+               "self.value_size!"
+        assert binary_value == self.binary_value, \
+               "packed binary value is not the same as " + \
+               "self.binary_value!"
 
     def getValue(self):
@@ -371,5 +367,5 @@
         return self.value_tag
 
-    def getSize(self):
+    def getValueSize(self):
         """
         Get the size of the value in bytes.
@@ -378,9 +374,16 @@
         return self.value_size
 
+    def getTagSize(self):
+        """
+        Get the size of the value tag in bytes.
+        """
+
+        return self.tag_size
+
     def setValue(self, value):
         """
         Set the non-binary value.
         """
-        
+
         self.value = value
 
@@ -389,4 +392,5 @@
         Set the binary value.
         """
+
         self.binary_value = binary_value
 
@@ -399,5 +403,5 @@
         self.value_tag = value_tag
 
-    def setSize(self, size):
+    def setValueSize(self, size):
         """
         Set the size of the value in bytes.
@@ -405,4 +409,11 @@
         
         self.value_size = size
+
+    def getTotalSize(self):
+        """
+        Get the total size of the IPP value.
+        """
+
+        return self.value_size + self.tag_size
 
     def __str__(self):
