Skip to content

Commit

Permalink
Worked on NSKeyedArchiver decode script
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimmetz committed Feb 18, 2024
1 parent 52dd23a commit e8e70da
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 15 deletions.
23 changes: 12 additions & 11 deletions plistrc/decoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import plistlib
import uuid

from dfdatetime import cocoa_time as dfdatetime_cocoa_time


class NSKeyedArchiverDecoder(object):
"""Decoder for NSKeyedArchiver encoded plists.
Expand Down Expand Up @@ -81,7 +83,6 @@ def _DecodeNSArray(self, plist_property, objects_array, parent_objects):
RuntimeError: if the NSArray or NSSet cannot be decoded.
"""
class_name = self._GetClassName(plist_property, objects_array)

if 'NS.objects' not in plist_property:
raise RuntimeError(f'Missing NS.objects in {class_name:s}')

Expand Down Expand Up @@ -128,7 +129,6 @@ def _DecodeNSData(self, plist_property, objects_array, parent_objects):
RuntimeError: if the NSData cannot be decoded.
"""
class_name = self._GetClassName(plist_property, objects_array)

if 'NS.data' not in plist_property:
raise RuntimeError(f'Missing NS.data in {class_name:s}')

Expand All @@ -150,13 +150,12 @@ def _DecodeNSDate(self, plist_property, objects_array, parent_objects):
parent_objects (list[int]): parent object UIDs.
Returns:
bytes: decoded NSDate.
dfdatetime.CocoaTime: decoded NSDate.
Raises:
RuntimeError: if the NSDate cannot be decoded.
"""
class_name = self._GetClassName(plist_property, objects_array)

if 'NS.time' not in plist_property:
raise RuntimeError(f'Missing NS.time in {class_name:s}')

Expand All @@ -167,7 +166,7 @@ def _DecodeNSDate(self, plist_property, objects_array, parent_objects):
raise RuntimeError(
f'Unsupported type: {type_string!s} in {class_name:s}.NS.time.')

return ns_time
return dfdatetime_cocoa_time.CocoaTime(timestamp=ns_time)

# pylint: enable=unused-argument

Expand All @@ -186,7 +185,6 @@ def _DecodeNSDictionary(self, plist_property, objects_array, parent_objects):
RuntimeError: if the NSDictionary cannot be decoded.
"""
class_name = self._GetClassName(plist_property, objects_array)

if 'NS.keys' not in plist_property or 'NS.objects' not in plist_property:
raise RuntimeError(f'Missing NS.keys or NS.objects in {class_name:s}')

Expand Down Expand Up @@ -263,7 +261,6 @@ def _DecodeNSHashTable(self, plist_property, objects_array, parent_objects):
RuntimeError: if the NSHashTable cannot be decoded.
"""
class_name = self._GetClassName(plist_property, objects_array)

if '$1' not in plist_property:
raise RuntimeError(f'Missing $1 in {class_name:s}')

Expand Down Expand Up @@ -322,7 +319,7 @@ def _DecodeNSObject(self, plist_property, objects_array, parent_objects):
parent_objects (list[int]): parent object UIDs.
Returns:
object: decoded object.
object: decoded NSObject.
Raises:
RuntimeError: if the NSObject cannot be decoded.
Expand Down Expand Up @@ -380,7 +377,6 @@ def _DecodeNSString(self, plist_property, objects_array, parent_objects):
RuntimeError: if the NSString cannot be decoded.
"""
class_name = self._GetClassName(plist_property, objects_array)

if 'NS.string' not in plist_property:
raise RuntimeError(f'Missing NS.string in {class_name:s}')

Expand All @@ -402,7 +398,7 @@ def _DecodeNSURL(self, plist_property, objects_array, parent_objects):
parent_objects (list[int]): parent object UIDs.
Returns:
object: decoded object.
str: decoded NSURL.
Raises:
RuntimeError: if the NSURL cannot be decoded.
Expand Down Expand Up @@ -456,7 +452,7 @@ def _DecodeNSUUID(self, plist_property, objects_array, parent_objects):
parent_objects (list[int]): parent object UIDs.
Returns:
object: decoded object.
str: decoded NSUUID.
Raises:
RuntimeError: if the NSUUID cannot be decoded.
Expand Down Expand Up @@ -586,6 +582,11 @@ def Decode(self, root_item):
decoded_object[name] = self._DecodeNSObject(
value_referenced_property, objects_array, [value_plist_uid])

# The root $top appears to be internal only to the NSKeyedArchiver encoded
# plist.
if len(decoded_object) == 1:
decoded_object = decoded_object.get('root', decoded_object)

return decoded_object

def IsEncoded(self, root_item):
Expand Down
5 changes: 5 additions & 0 deletions scripts/decode.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import plistlib
import sys

from dfdatetime import cocoa_time as dfdatetime_cocoa_time

from plistrc import decoders


Expand All @@ -27,6 +29,9 @@ def default(self, o):
encoded_bytes = base64.urlsafe_b64encode(o)
return encoded_bytes.decode('latin1')

if isinstance(o, dfdatetime_cocoa_time.CocoaTime):
return o.timestamp

return super(NSKeyedArchiverJSONEncoder, self).default(o)


Expand Down
33 changes: 29 additions & 4 deletions tests/decoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@
class NSKeyedArchiverDecoderTest(test_lib.BaseTestCase):
"""Tests for the decoder for NSKeyedArchiver encoded plists."""

# TODO: add tests for _DecodeCompositeObject.
# TODO: add tests for _DecodeNSArray.
# TODO: add tests for _DecodeNSData.
# TODO: add tests for _DecodeNSDate.
# TODO: add tests for _DecodeNSDictionary.
# TODO: add tests for _DecodeNSHashTable.
# TODO: add tests for _DecodeNSNull.
# TODO: add tests for _DecodeNSObject.
# TODO: add tests for _DecodeNSString.
# TODO: add tests for _DecodeNSURL.
# TODO: add tests for _DecodeNSUUID.
# TODO: add tests for _DecodeObject.
# TODO: add tests for _GetClassName.
# TODO: add tests for _GetPlistUID.

def testDecode(self):
"""Tests the Decode function."""
test_file_path = self._GetTestFilePath(['NSKeyedArchiver.plist'])
Expand All @@ -25,11 +40,21 @@ def testDecode(self):

decoded_plist = test_decoder.Decode(encoded_plist)
self.assertIsNotNone(decoded_plist)
self.assertIn('root', decoded_plist)
self.assertIn('MyString', decoded_plist)
self.assertEqual(decoded_plist['MyString'], 'Some string')

def testIsEncoded(self):
"""Tests the IsEncoded function."""
test_file_path = self._GetTestFilePath(['NSKeyedArchiver.plist'])
self._SkipIfPathNotExists(test_file_path)

test_decoder = decoders.NSKeyedArchiverDecoder()

with open(test_file_path, 'rb') as file_object:
encoded_plist = plistlib.load(file_object)

root_item = decoded_plist['root']
self.assertIn('MyString', root_item)
self.assertEqual(root_item['MyString'], 'Some string')
result = test_decoder.IsEncoded(encoded_plist)
self.assertTrue(result)


if __name__ == '__main__':
Expand Down

0 comments on commit e8e70da

Please sign in to comment.