Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 37 additions & 29 deletions bplist/bplist.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,27 @@ class BPListWriter(object):
def __init__(self, objects):
self.bplist = ""
self.objects = objects

def binary(self):
'''binary -> string

Generates bplist
'''
self.data = 'bplist00'

# TODO: flatten objects and count max length size

# TODO: write objects and save offsets

# TODO: write offsets

# TODO: write metadata

return self.data

def write(self, filename):
'''

Writes bplist to file
'''
if self.bplist != "":
Expand All @@ -62,10 +62,18 @@ def __init__(self, s):
self.data = s
self.objects = []
self.resolved = {}


def __getObjHeader(self, offset):
obj_header = self.data[offset]

if type(obj_header) == str:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure we don't need that check because the BPlistReader() expects an str as input.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So reading from string never worked or something changed in python2?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a tricky question to answer now, given I never wrote unit tests for this :-)

I'm pretty sure all codepaths used to work, but I went back to python 2.5 and it's still a problem there. This package is clearly not the most popular one so maybe it was a bug all these years.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, I feel like it's safer to leave the check in there

obj_header = ord(obj_header)

return obj_header

def __unpackIntStruct(self, sz, s):
'''__unpackIntStruct(size, string) -> int

Unpacks the integer of given size (1, 2 or 4 bytes) from string
'''
if sz == 1:
Expand All @@ -79,27 +87,27 @@ def __unpackIntStruct(self, sz, s):
else:
raise Exception('int unpack size '+str(sz)+' unsupported')
return struct.unpack(ot, s)[0]

def __unpackInt(self, offset):
'''__unpackInt(offset) -> int

Unpacks int field from plist at given offset
'''
return self.__unpackIntMeta(offset)[1]

def __unpackIntMeta(self, offset):
'''__unpackIntMeta(offset) -> (size, int)

Unpacks int field from plist at given offset and returns its size and value
'''
obj_header = self.data[offset]
obj_header = self.__getObjHeader(offset)
obj_type, obj_info = (obj_header & 0xF0), (obj_header & 0x0F)
int_sz = 2**obj_info
return int_sz, self.__unpackIntStruct(int_sz, self.data[offset+1:offset+1+int_sz])

def __resolveIntSize(self, obj_info, offset):
'''__resolveIntSize(obj_info, offset) -> (count, offset)

Calculates count of objref* array entries and returns count and offset to first element
'''
if obj_info == 0x0F:
Expand All @@ -112,7 +120,7 @@ def __resolveIntSize(self, obj_info, offset):

def __unpackFloatStruct(self, sz, s):
'''__unpackFloatStruct(size, string) -> float

Unpacks the float of given size (4 or 8 bytes) from string
'''
if sz == 4:
Expand All @@ -125,10 +133,10 @@ def __unpackFloatStruct(self, sz, s):

def __unpackFloat(self, offset):
'''__unpackFloat(offset) -> float

Unpacks float field from plist at given offset
'''
obj_header = self.data[offset]
obj_header = self.__getObjHeader(offset)
obj_type, obj_info = (obj_header & 0xF0), (obj_header & 0x0F)
int_sz = 2**obj_info
return int_sz, self.__unpackFloatStruct(int_sz, self.data[offset+1:offset+1+int_sz])
Expand All @@ -139,10 +147,10 @@ def __unpackDate(self, offset):

def __unpackItem(self, offset):
'''__unpackItem(offset)

Unpacks and returns an item from plist
'''
obj_header = self.data[offset]
obj_header = self.__getObjHeader(offset)
obj_type, obj_info = (obj_header & 0xF0), (obj_header & 0x0F)
if obj_type == 0x00:
if obj_info == 0x00: # null 0000 0000
Expand Down Expand Up @@ -198,7 +206,7 @@ def __unpackItem(self, offset):
return dic
else:
raise Exception('don\'t know how to unpack obj type '+hex(obj_type)+' at '+str(offset))

def __resolveObject(self, idx):
try:
return self.resolved[idx]
Expand All @@ -225,16 +233,16 @@ def __resolveObject(self, idx):
else:
self.resolved[idx] = obj
return obj

def parse(self):
# read header
if self.data[:8] != b'bplist00':
raise Exception('Bad magic')

# read trailer
self.offset_size, self.object_ref_size, self.number_of_objects, self.top_object, self.table_offset = struct.unpack('!6xBB4xI4xI4xI', self.data[-32:])
#print "** plist offset_size:",self.offset_size,"objref_size:",self.object_ref_size,"num_objs:",self.number_of_objects,"top:",self.top_object,"table_ofs:",self.table_offset

# read offset table
self.offset_table = self.data[self.table_offset:-32]
self.offsets = []
Expand All @@ -244,7 +252,7 @@ def parse(self):
ot = ot[self.offset_size:]
self.offsets.append(self.__unpackIntStruct(self.offset_size, offset_entry))
#print "** plist offsets:",self.offsets

# read object table
self.objects = []
k = 0
Expand All @@ -253,14 +261,14 @@ def parse(self):
#print "** plist unpacked",k,type(obj),obj,"at",i
k += 1
self.objects.append(obj)

# rebuild object tree
#for i in range(len(self.objects)):
# self.__resolveObject(i)

# return root object
return self.__resolveObject(self.top_object)

@classmethod
def plistWithString(cls, s):
parser = cls(s)
Expand Down