1ef3e4c2b4d5f07acc33c66d01063fcdf00b7b7d9Benjamin Petersonr"""plistlib.py -- a tool to generate and parse MacOSX .plist files. 27e182548faeabc526f6eed546933e9745949f584Christian Heimes 36e9b1df499ef87bf8b21787219fbccee8c0075d7Ezio MelottiThe property list (.plist) file format is a simple XML pickle supporting 47e182548faeabc526f6eed546933e9745949f584Christian Heimesbasic object types, like dictionaries, lists, numbers and strings. 57e182548faeabc526f6eed546933e9745949f584Christian HeimesUsually the top level object is a dictionary. 67e182548faeabc526f6eed546933e9745949f584Christian Heimes 7c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald OussorenTo write out a plist file, use the dump(value, file) 8c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenfunction. 'value' is the top level object, 'file' is 9c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorena (writable) file object. 107e182548faeabc526f6eed546933e9745949f584Christian Heimes 11c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald OussorenTo parse a plist from a file, use the load(file) function, 12c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenwith a (readable) file object as the only argument. It 137e182548faeabc526f6eed546933e9745949f584Christian Heimesreturns the top level object (again, usually a dictionary). 147e182548faeabc526f6eed546933e9745949f584Christian Heimes 15c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald OussorenTo work with plist data in bytes objects, you can use loads() 16c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenand dumps(). 177e182548faeabc526f6eed546933e9745949f584Christian Heimes 187e182548faeabc526f6eed546933e9745949f584Christian HeimesValues can be strings, integers, floats, booleans, tuples, lists, 19c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendictionaries (but only with string keys), Data, bytes, bytearray, or 20c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendatetime.datetime objects. 217e182548faeabc526f6eed546933e9745949f584Christian Heimes 227e182548faeabc526f6eed546933e9745949f584Christian HeimesGenerate Plist example: 237e182548faeabc526f6eed546933e9745949f584Christian Heimes 247e182548faeabc526f6eed546933e9745949f584Christian Heimes pl = dict( 256e9b1df499ef87bf8b21787219fbccee8c0075d7Ezio Melotti aString = "Doodah", 266e9b1df499ef87bf8b21787219fbccee8c0075d7Ezio Melotti aList = ["A", "B", 12, 32.1, [1, 2, 3]], 277e182548faeabc526f6eed546933e9745949f584Christian Heimes aFloat = 0.1, 287e182548faeabc526f6eed546933e9745949f584Christian Heimes anInt = 728, 296e9b1df499ef87bf8b21787219fbccee8c0075d7Ezio Melotti aDict = dict( 306e9b1df499ef87bf8b21787219fbccee8c0075d7Ezio Melotti anotherString = "<hello & hi there!>", 316e9b1df499ef87bf8b21787219fbccee8c0075d7Ezio Melotti aUnicodeValue = "M\xe4ssig, Ma\xdf", 326e9b1df499ef87bf8b21787219fbccee8c0075d7Ezio Melotti aTrueValue = True, 336e9b1df499ef87bf8b21787219fbccee8c0075d7Ezio Melotti aFalseValue = False, 347e182548faeabc526f6eed546933e9745949f584Christian Heimes ), 35c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren someData = b"<binary gunk>", 36c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren someMoreData = b"<lots of binary gunk>" * 10, 377e182548faeabc526f6eed546933e9745949f584Christian Heimes aDate = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())), 387e182548faeabc526f6eed546933e9745949f584Christian Heimes ) 39c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren with open(fileName, 'wb') as fp: 40c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren dump(pl, fp) 417e182548faeabc526f6eed546933e9745949f584Christian Heimes 427e182548faeabc526f6eed546933e9745949f584Christian HeimesParse Plist example: 437e182548faeabc526f6eed546933e9745949f584Christian Heimes 44c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren with open(fileName, 'rb') as fp: 45c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren pl = load(fp) 46c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren print(pl["aKey"]) 477e182548faeabc526f6eed546933e9745949f584Christian Heimes""" 487e182548faeabc526f6eed546933e9745949f584Christian Heimes__all__ = [ 497e182548faeabc526f6eed546933e9745949f584Christian Heimes "readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes", 50d04d21373f01ff0d4b8aabfb73ea19b2c023f4a0Martin Panter "Plist", "Data", "Dict", "InvalidFileException", "FMT_XML", "FMT_BINARY", 51c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren "load", "dump", "loads", "dumps" 527e182548faeabc526f6eed546933e9745949f584Christian Heimes] 537e182548faeabc526f6eed546933e9745949f584Christian Heimes 547e182548faeabc526f6eed546933e9745949f584Christian Heimesimport binascii 55c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenimport codecs 56c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenimport contextlib 577e182548faeabc526f6eed546933e9745949f584Christian Heimesimport datetime 58c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenimport enum 597e182548faeabc526f6eed546933e9745949f584Christian Heimesfrom io import BytesIO 60c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenimport itertools 61c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenimport os 627e182548faeabc526f6eed546933e9745949f584Christian Heimesimport re 63c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenimport struct 64c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenfrom warnings import warn 65c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenfrom xml.parsers.expat import ParserCreate 667e182548faeabc526f6eed546933e9745949f584Christian Heimes 677e182548faeabc526f6eed546933e9745949f584Christian Heimes 68c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald OussorenPlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__) 69c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenglobals().update(PlistFormat.__members__) 707e182548faeabc526f6eed546933e9745949f584Christian Heimes 717e182548faeabc526f6eed546933e9745949f584Christian Heimes 72c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 73c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 74c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# Deprecated functionality 75c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 76c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 777e182548faeabc526f6eed546933e9745949f584Christian Heimes 787e182548faeabc526f6eed546933e9745949f584Christian Heimes 797e182548faeabc526f6eed546933e9745949f584Christian Heimesclass _InternalDict(dict): 807e182548faeabc526f6eed546933e9745949f584Christian Heimes 817e182548faeabc526f6eed546933e9745949f584Christian Heimes # This class is needed while Dict is scheduled for deprecation: 827e182548faeabc526f6eed546933e9745949f584Christian Heimes # we only need to warn when a *user* instantiates Dict or when 837e182548faeabc526f6eed546933e9745949f584Christian Heimes # the "attribute notation for dict keys" is used. 84c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren __slots__ = () 857e182548faeabc526f6eed546933e9745949f584Christian Heimes 867e182548faeabc526f6eed546933e9745949f584Christian Heimes def __getattr__(self, attr): 877e182548faeabc526f6eed546933e9745949f584Christian Heimes try: 887e182548faeabc526f6eed546933e9745949f584Christian Heimes value = self[attr] 897e182548faeabc526f6eed546933e9745949f584Christian Heimes except KeyError: 907e182548faeabc526f6eed546933e9745949f584Christian Heimes raise AttributeError(attr) 917e182548faeabc526f6eed546933e9745949f584Christian Heimes warn("Attribute access from plist dicts is deprecated, use d[key] " 92b575289292902bb078dbd128a90da642c27d4ad6Victor Stinner "notation instead", DeprecationWarning, 2) 937e182548faeabc526f6eed546933e9745949f584Christian Heimes return value 947e182548faeabc526f6eed546933e9745949f584Christian Heimes 957e182548faeabc526f6eed546933e9745949f584Christian Heimes def __setattr__(self, attr, value): 967e182548faeabc526f6eed546933e9745949f584Christian Heimes warn("Attribute access from plist dicts is deprecated, use d[key] " 97b575289292902bb078dbd128a90da642c27d4ad6Victor Stinner "notation instead", DeprecationWarning, 2) 987e182548faeabc526f6eed546933e9745949f584Christian Heimes self[attr] = value 997e182548faeabc526f6eed546933e9745949f584Christian Heimes 1007e182548faeabc526f6eed546933e9745949f584Christian Heimes def __delattr__(self, attr): 1017e182548faeabc526f6eed546933e9745949f584Christian Heimes try: 1027e182548faeabc526f6eed546933e9745949f584Christian Heimes del self[attr] 1037e182548faeabc526f6eed546933e9745949f584Christian Heimes except KeyError: 1047e182548faeabc526f6eed546933e9745949f584Christian Heimes raise AttributeError(attr) 1057e182548faeabc526f6eed546933e9745949f584Christian Heimes warn("Attribute access from plist dicts is deprecated, use d[key] " 106b575289292902bb078dbd128a90da642c27d4ad6Victor Stinner "notation instead", DeprecationWarning, 2) 1077e182548faeabc526f6eed546933e9745949f584Christian Heimes 108c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 1097e182548faeabc526f6eed546933e9745949f584Christian Heimesclass Dict(_InternalDict): 1107e182548faeabc526f6eed546933e9745949f584Christian Heimes 1117e182548faeabc526f6eed546933e9745949f584Christian Heimes def __init__(self, **kwargs): 1127e182548faeabc526f6eed546933e9745949f584Christian Heimes warn("The plistlib.Dict class is deprecated, use builtin dict instead", 113b575289292902bb078dbd128a90da642c27d4ad6Victor Stinner DeprecationWarning, 2) 1147e182548faeabc526f6eed546933e9745949f584Christian Heimes super().__init__(**kwargs) 1157e182548faeabc526f6eed546933e9745949f584Christian Heimes 1167e182548faeabc526f6eed546933e9745949f584Christian Heimes 117c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren@contextlib.contextmanager 118c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef _maybe_open(pathOrFile, mode): 119c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if isinstance(pathOrFile, str): 120c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren with open(pathOrFile, mode) as fp: 121c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren yield fp 122c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 123c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 124c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren yield pathOrFile 125c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 1267e182548faeabc526f6eed546933e9745949f584Christian Heimes 127c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenclass Plist(_InternalDict): 128c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """This class has been deprecated. Use dump() and load() 1297e182548faeabc526f6eed546933e9745949f584Christian Heimes functions instead, together with regular dict objects. 1307e182548faeabc526f6eed546933e9745949f584Christian Heimes """ 1317e182548faeabc526f6eed546933e9745949f584Christian Heimes 1327e182548faeabc526f6eed546933e9745949f584Christian Heimes def __init__(self, **kwargs): 133c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren warn("The Plist class is deprecated, use the load() and " 134c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren "dump() functions instead", DeprecationWarning, 2) 1357e182548faeabc526f6eed546933e9745949f584Christian Heimes super().__init__(**kwargs) 1367e182548faeabc526f6eed546933e9745949f584Christian Heimes 137c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren @classmethod 1387e182548faeabc526f6eed546933e9745949f584Christian Heimes def fromFile(cls, pathOrFile): 139c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """Deprecated. Use the load() function instead.""" 140c06d6fdc37a70c28feaac60b06047b9f6357c05fNed Deily with _maybe_open(pathOrFile, 'rb') as fp: 141c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren value = load(fp) 1427e182548faeabc526f6eed546933e9745949f584Christian Heimes plist = cls() 143c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren plist.update(value) 1447e182548faeabc526f6eed546933e9745949f584Christian Heimes return plist 1457e182548faeabc526f6eed546933e9745949f584Christian Heimes 1467e182548faeabc526f6eed546933e9745949f584Christian Heimes def write(self, pathOrFile): 147c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """Deprecated. Use the dump() function instead.""" 148c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren with _maybe_open(pathOrFile, 'wb') as fp: 149c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren dump(self, fp) 1507e182548faeabc526f6eed546933e9745949f584Christian Heimes 1517e182548faeabc526f6eed546933e9745949f584Christian Heimes 152c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef readPlist(pathOrFile): 153c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 154c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren Read a .plist from a path or file. pathOrFile should either 155c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren be a file name, or a readable binary file object. 156c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 157c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren This function is deprecated, use load instead. 158c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 159c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren warn("The readPlist function is deprecated, use load() instead", 160c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren DeprecationWarning, 2) 161c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 162c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren with _maybe_open(pathOrFile, 'rb') as fp: 163c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return load(fp, fmt=None, use_builtin_types=False, 164c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren dict_type=_InternalDict) 165c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 166c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef writePlist(value, pathOrFile): 167c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 168c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren Write 'value' to a .plist file. 'pathOrFile' may either be a 169c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren file name or a (writable) file object. 170c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 171c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren This function is deprecated, use dump instead. 172c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 173c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren warn("The writePlist function is deprecated, use dump() instead", 174c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren DeprecationWarning, 2) 175c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren with _maybe_open(pathOrFile, 'wb') as fp: 176c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren dump(value, fp, fmt=FMT_XML, sort_keys=True, skipkeys=False) 177c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 178c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 179c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef readPlistFromBytes(data): 180c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 181c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren Read a plist data from a bytes object. Return the root object. 182c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 183c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren This function is deprecated, use loads instead. 184c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 185c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren warn("The readPlistFromBytes function is deprecated, use loads() instead", 186c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren DeprecationWarning, 2) 187c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return load(BytesIO(data), fmt=None, use_builtin_types=False, 188c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren dict_type=_InternalDict) 189c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 190c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 191c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef writePlistToBytes(value): 192c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 193c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren Return 'value' as a plist-formatted bytes object. 194c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 195c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren This function is deprecated, use dumps instead. 196c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 197c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren warn("The writePlistToBytes function is deprecated, use dumps() instead", 198c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren DeprecationWarning, 2) 199c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren f = BytesIO() 200c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren dump(value, f, fmt=FMT_XML, sort_keys=True, skipkeys=False) 201c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return f.getvalue() 202c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 2037e182548faeabc526f6eed546933e9745949f584Christian Heimes 2047e182548faeabc526f6eed546933e9745949f584Christian Heimesclass Data: 205c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 206c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren Wrapper for binary data. 2077e182548faeabc526f6eed546933e9745949f584Christian Heimes 208c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren This class is deprecated, use a bytes object instead. 209c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 2107e182548faeabc526f6eed546933e9745949f584Christian Heimes 2117e182548faeabc526f6eed546933e9745949f584Christian Heimes def __init__(self, data): 2127e182548faeabc526f6eed546933e9745949f584Christian Heimes if not isinstance(data, bytes): 2137e182548faeabc526f6eed546933e9745949f584Christian Heimes raise TypeError("data must be as bytes") 2147e182548faeabc526f6eed546933e9745949f584Christian Heimes self.data = data 2157e182548faeabc526f6eed546933e9745949f584Christian Heimes 2167e182548faeabc526f6eed546933e9745949f584Christian Heimes @classmethod 2177e182548faeabc526f6eed546933e9745949f584Christian Heimes def fromBase64(cls, data): 218706824f19f734e5e567f32d989376547c0ae08daGeorg Brandl # base64.decodebytes just calls binascii.a2b_base64; 2197e182548faeabc526f6eed546933e9745949f584Christian Heimes # it seems overkill to use both base64 and binascii. 220c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return cls(_decode_base64(data)) 2217e182548faeabc526f6eed546933e9745949f584Christian Heimes 2227e182548faeabc526f6eed546933e9745949f584Christian Heimes def asBase64(self, maxlinelength=76): 223c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return _encode_base64(self.data, maxlinelength) 2247e182548faeabc526f6eed546933e9745949f584Christian Heimes 2257e182548faeabc526f6eed546933e9745949f584Christian Heimes def __eq__(self, other): 2267e182548faeabc526f6eed546933e9745949f584Christian Heimes if isinstance(other, self.__class__): 2277e182548faeabc526f6eed546933e9745949f584Christian Heimes return self.data == other.data 228dd1bcdf6187ff18fb1536a6190926b0f03336186Serhiy Storchaka elif isinstance(other, bytes): 2297e182548faeabc526f6eed546933e9745949f584Christian Heimes return self.data == other 2307e182548faeabc526f6eed546933e9745949f584Christian Heimes else: 231dd1bcdf6187ff18fb1536a6190926b0f03336186Serhiy Storchaka return NotImplemented 2327e182548faeabc526f6eed546933e9745949f584Christian Heimes 2337e182548faeabc526f6eed546933e9745949f584Christian Heimes def __repr__(self): 2347e182548faeabc526f6eed546933e9745949f584Christian Heimes return "%s(%s)" % (self.__class__.__name__, repr(self.data)) 2357e182548faeabc526f6eed546933e9745949f584Christian Heimes 236c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 237c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 238c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# End of deprecated functionality 239c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 240c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 241c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 242c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 243c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 244c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# XML support 245c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 246c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 247c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 248c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# XML 'header' 249c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald OussorenPLISTHEADER = b"""\ 250c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren<?xml version="1.0" encoding="UTF-8"?> 251c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 252c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren""" 253c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 254c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 255c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# Regex to find any control chars, except for \t \n and \r 256c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren_controlCharPat = re.compile( 257c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren r"[\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f" 258c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren r"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f]") 259c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 260c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef _encode_base64(s, maxlinelength=76): 261c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # copied from base64.encodebytes(), with added maxlinelength argument 262c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren maxbinsize = (maxlinelength//4)*3 263c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren pieces = [] 264c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for i in range(0, len(s), maxbinsize): 265c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren chunk = s[i : i + maxbinsize] 266c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren pieces.append(binascii.b2a_base64(chunk)) 267c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return b''.join(pieces) 268c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 269c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef _decode_base64(s): 270c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if isinstance(s, str): 271c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return binascii.a2b_base64(s.encode("utf-8")) 272c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 273c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 274c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return binascii.a2b_base64(s) 2757e182548faeabc526f6eed546933e9745949f584Christian Heimes 276c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# Contents should conform to a subset of ISO 8601 277c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# (in particular, YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'. Smaller units 278c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# may be omitted with # a loss of precision) 279c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren_dateParser = re.compile(r"(?P<year>\d\d\d\d)(?:-(?P<month>\d\d)(?:-(?P<day>\d\d)(?:T(?P<hour>\d\d)(?::(?P<minute>\d\d)(?::(?P<second>\d\d))?)?)?)?)?Z", re.ASCII) 280c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 281c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 282c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef _date_from_string(s): 283c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren order = ('year', 'month', 'day', 'hour', 'minute', 'second') 284c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren gd = _dateParser.match(s).groupdict() 285c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren lst = [] 286c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for key in order: 287c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren val = gd[key] 288c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if val is None: 289c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren break 290c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren lst.append(int(val)) 291c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return datetime.datetime(*lst) 292c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 293c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 294c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef _date_to_string(d): 295c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return '%04d-%02d-%02dT%02d:%02d:%02dZ' % ( 296c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren d.year, d.month, d.day, 297c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren d.hour, d.minute, d.second 298c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren ) 299c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 300c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef _escape(text): 301c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren m = _controlCharPat.search(text) 302c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if m is not None: 303c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren raise ValueError("strings can't contains control characters; " 304c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren "use bytes instead") 305c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren text = text.replace("\r\n", "\n") # convert DOS line endings 306c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren text = text.replace("\r", "\n") # convert Mac line endings 307c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren text = text.replace("&", "&") # escape '&' 308c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren text = text.replace("<", "<") # escape '<' 309c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren text = text.replace(">", ">") # escape '>' 310c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return text 311c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 312c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenclass _PlistParser: 313c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def __init__(self, use_builtin_types, dict_type): 3147e182548faeabc526f6eed546933e9745949f584Christian Heimes self.stack = [] 315c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.current_key = None 3167e182548faeabc526f6eed546933e9745949f584Christian Heimes self.root = None 317c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._use_builtin_types = use_builtin_types 318c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._dict_type = dict_type 3197e182548faeabc526f6eed546933e9745949f584Christian Heimes 3207e182548faeabc526f6eed546933e9745949f584Christian Heimes def parse(self, fileobj): 321b8e59f77e65ba4caeda8d910bd66df01a468cbeaNed Deily self.parser = ParserCreate() 322c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.parser.StartElementHandler = self.handle_begin_element 323c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.parser.EndElementHandler = self.handle_end_element 324c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.parser.CharacterDataHandler = self.handle_data 325b8e59f77e65ba4caeda8d910bd66df01a468cbeaNed Deily self.parser.ParseFile(fileobj) 3267e182548faeabc526f6eed546933e9745949f584Christian Heimes return self.root 3277e182548faeabc526f6eed546933e9745949f584Christian Heimes 328c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def handle_begin_element(self, element, attrs): 3297e182548faeabc526f6eed546933e9745949f584Christian Heimes self.data = [] 3307e182548faeabc526f6eed546933e9745949f584Christian Heimes handler = getattr(self, "begin_" + element, None) 3317e182548faeabc526f6eed546933e9745949f584Christian Heimes if handler is not None: 3327e182548faeabc526f6eed546933e9745949f584Christian Heimes handler(attrs) 3337e182548faeabc526f6eed546933e9745949f584Christian Heimes 334c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def handle_end_element(self, element): 3357e182548faeabc526f6eed546933e9745949f584Christian Heimes handler = getattr(self, "end_" + element, None) 3367e182548faeabc526f6eed546933e9745949f584Christian Heimes if handler is not None: 3377e182548faeabc526f6eed546933e9745949f584Christian Heimes handler() 3387e182548faeabc526f6eed546933e9745949f584Christian Heimes 339c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def handle_data(self, data): 3407e182548faeabc526f6eed546933e9745949f584Christian Heimes self.data.append(data) 3417e182548faeabc526f6eed546933e9745949f584Christian Heimes 342c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def add_object(self, value): 343c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if self.current_key is not None: 344b8e59f77e65ba4caeda8d910bd66df01a468cbeaNed Deily if not isinstance(self.stack[-1], type({})): 345b8e59f77e65ba4caeda8d910bd66df01a468cbeaNed Deily raise ValueError("unexpected element at line %d" % 346b8e59f77e65ba4caeda8d910bd66df01a468cbeaNed Deily self.parser.CurrentLineNumber) 347c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.stack[-1][self.current_key] = value 348c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.current_key = None 3497e182548faeabc526f6eed546933e9745949f584Christian Heimes elif not self.stack: 3507e182548faeabc526f6eed546933e9745949f584Christian Heimes # this is the root object 3517e182548faeabc526f6eed546933e9745949f584Christian Heimes self.root = value 3527e182548faeabc526f6eed546933e9745949f584Christian Heimes else: 353b8e59f77e65ba4caeda8d910bd66df01a468cbeaNed Deily if not isinstance(self.stack[-1], type([])): 354b8e59f77e65ba4caeda8d910bd66df01a468cbeaNed Deily raise ValueError("unexpected element at line %d" % 355b8e59f77e65ba4caeda8d910bd66df01a468cbeaNed Deily self.parser.CurrentLineNumber) 3567e182548faeabc526f6eed546933e9745949f584Christian Heimes self.stack[-1].append(value) 3577e182548faeabc526f6eed546933e9745949f584Christian Heimes 358c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def get_data(self): 3597e182548faeabc526f6eed546933e9745949f584Christian Heimes data = ''.join(self.data) 3607e182548faeabc526f6eed546933e9745949f584Christian Heimes self.data = [] 3617e182548faeabc526f6eed546933e9745949f584Christian Heimes return data 3627e182548faeabc526f6eed546933e9745949f584Christian Heimes 3637e182548faeabc526f6eed546933e9745949f584Christian Heimes # element handlers 3647e182548faeabc526f6eed546933e9745949f584Christian Heimes 3657e182548faeabc526f6eed546933e9745949f584Christian Heimes def begin_dict(self, attrs): 366c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren d = self._dict_type() 367c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.add_object(d) 3687e182548faeabc526f6eed546933e9745949f584Christian Heimes self.stack.append(d) 369c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 3707e182548faeabc526f6eed546933e9745949f584Christian Heimes def end_dict(self): 371c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if self.current_key: 372b8e59f77e65ba4caeda8d910bd66df01a468cbeaNed Deily raise ValueError("missing value for key '%s' at line %d" % 373c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren (self.current_key,self.parser.CurrentLineNumber)) 3747e182548faeabc526f6eed546933e9745949f584Christian Heimes self.stack.pop() 3757e182548faeabc526f6eed546933e9745949f584Christian Heimes 3767e182548faeabc526f6eed546933e9745949f584Christian Heimes def end_key(self): 377c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if self.current_key or not isinstance(self.stack[-1], type({})): 378b8e59f77e65ba4caeda8d910bd66df01a468cbeaNed Deily raise ValueError("unexpected key at line %d" % 379b8e59f77e65ba4caeda8d910bd66df01a468cbeaNed Deily self.parser.CurrentLineNumber) 380c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.current_key = self.get_data() 3817e182548faeabc526f6eed546933e9745949f584Christian Heimes 3827e182548faeabc526f6eed546933e9745949f584Christian Heimes def begin_array(self, attrs): 3837e182548faeabc526f6eed546933e9745949f584Christian Heimes a = [] 384c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.add_object(a) 3857e182548faeabc526f6eed546933e9745949f584Christian Heimes self.stack.append(a) 386c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 3877e182548faeabc526f6eed546933e9745949f584Christian Heimes def end_array(self): 3887e182548faeabc526f6eed546933e9745949f584Christian Heimes self.stack.pop() 3897e182548faeabc526f6eed546933e9745949f584Christian Heimes 3907e182548faeabc526f6eed546933e9745949f584Christian Heimes def end_true(self): 391c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.add_object(True) 392c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 3937e182548faeabc526f6eed546933e9745949f584Christian Heimes def end_false(self): 394c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.add_object(False) 395c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 3967e182548faeabc526f6eed546933e9745949f584Christian Heimes def end_integer(self): 397c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.add_object(int(self.get_data())) 398c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 3997e182548faeabc526f6eed546933e9745949f584Christian Heimes def end_real(self): 400c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.add_object(float(self.get_data())) 401c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 4027e182548faeabc526f6eed546933e9745949f584Christian Heimes def end_string(self): 403c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.add_object(self.get_data()) 404c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 4057e182548faeabc526f6eed546933e9745949f584Christian Heimes def end_data(self): 406c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if self._use_builtin_types: 407c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.add_object(_decode_base64(self.get_data())) 408c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 409c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 410c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.add_object(Data.fromBase64(self.get_data())) 411c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 4127e182548faeabc526f6eed546933e9745949f584Christian Heimes def end_date(self): 413c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.add_object(_date_from_string(self.get_data())) 414c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 415c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 416c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenclass _DumbXMLWriter: 417c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def __init__(self, file, indent_level=0, indent="\t"): 418c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.file = file 419c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.stack = [] 420c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._indent_level = indent_level 421c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.indent = indent 422c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 423c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def begin_element(self, element): 424c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.stack.append(element) 425c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.writeln("<%s>" % element) 426c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._indent_level += 1 427c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 428c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def end_element(self, element): 429c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren assert self._indent_level > 0 430c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren assert self.stack.pop() == element 431c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._indent_level -= 1 432c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.writeln("</%s>" % element) 433c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 434c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def simple_element(self, element, value=None): 435c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if value is not None: 436c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren value = _escape(value) 437c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.writeln("<%s>%s</%s>" % (element, value, element)) 438c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 439c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 440c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.writeln("<%s/>" % element) 441c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 442c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def writeln(self, line): 443c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if line: 444c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # plist has fixed encoding of utf-8 445c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 446c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # XXX: is this test needed? 447c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if isinstance(line, str): 448c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren line = line.encode('utf-8') 449c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.file.write(self._indent_level * self.indent) 450c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.file.write(line) 451c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.file.write(b'\n') 452c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 453c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 454c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenclass _PlistWriter(_DumbXMLWriter): 455c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def __init__( 456c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self, file, indent_level=0, indent=b"\t", writeHeader=1, 457c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren sort_keys=True, skipkeys=False): 458c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 459c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if writeHeader: 460c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren file.write(PLISTHEADER) 461c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren _DumbXMLWriter.__init__(self, file, indent_level, indent) 462c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._sort_keys = sort_keys 463c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._skipkeys = skipkeys 464c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 465c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def write(self, value): 466c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.writeln("<plist version=\"1.0\">") 467c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.write_value(value) 468c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.writeln("</plist>") 469c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 470c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def write_value(self, value): 471c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if isinstance(value, str): 472c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.simple_element("string", value) 473c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 474c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif value is True: 475c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.simple_element("true") 476c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 477c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif value is False: 478c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.simple_element("false") 479c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 480c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, int): 4816db6653bbc0841600248e4d7b7542591067c4157Ronald Oussoren if -1 << 63 <= value < 1 << 64: 4826db6653bbc0841600248e4d7b7542591067c4157Ronald Oussoren self.simple_element("integer", "%d" % value) 4836db6653bbc0841600248e4d7b7542591067c4157Ronald Oussoren else: 4846db6653bbc0841600248e4d7b7542591067c4157Ronald Oussoren raise OverflowError(value) 485c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 486c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, float): 487c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.simple_element("real", repr(value)) 488c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 489c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, dict): 490c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.write_dict(value) 491c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 492c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, Data): 493c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.write_data(value) 494c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 495c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, (bytes, bytearray)): 496c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.write_bytes(value) 497c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 498c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, datetime.datetime): 499c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.simple_element("date", _date_to_string(value)) 500c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 501c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, (tuple, list)): 502c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.write_array(value) 503c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 504c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 505c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren raise TypeError("unsupported type: %s" % type(value)) 506c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 507c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def write_data(self, data): 508c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.write_bytes(data.data) 509c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 510c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def write_bytes(self, data): 511c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.begin_element("data") 512c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._indent_level -= 1 513c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren maxlinelength = max( 514c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 16, 515c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 76 - len(self.indent.replace(b"\t", b" " * 8) * self._indent_level)) 516c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 517c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for line in _encode_base64(data, maxlinelength).split(b"\n"): 518c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if line: 519c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.writeln(line) 520c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._indent_level += 1 521c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.end_element("data") 522c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 523c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def write_dict(self, d): 524c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if d: 525c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.begin_element("dict") 526c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if self._sort_keys: 527c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren items = sorted(d.items()) 528c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 529c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren items = d.items() 530c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 531c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for key, value in items: 532c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if not isinstance(key, str): 533c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if self._skipkeys: 534c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren continue 535c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren raise TypeError("keys must be strings") 536c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.simple_element("key", key) 537c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.write_value(value) 538c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.end_element("dict") 539c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 540c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 541c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.simple_element("dict") 542c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 543c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def write_array(self, array): 544c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if array: 545c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.begin_element("array") 546c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for value in array: 547c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.write_value(value) 548c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.end_element("array") 549c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 550c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 551c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self.simple_element("array") 552c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 553c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 554c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef _is_fmt_xml(header): 555c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren prefixes = (b'<?xml', b'<plist') 556c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 557c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for pfx in prefixes: 558c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if header.startswith(pfx): 559c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return True 560c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 561c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # Also check for alternative XML encodings, this is slightly 562c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # overkill because the Apple tools (and plistlib) will not 563c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # generate files with these encodings. 564c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for bom, encoding in ( 565c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren (codecs.BOM_UTF8, "utf-8"), 566c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren (codecs.BOM_UTF16_BE, "utf-16-be"), 567c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren (codecs.BOM_UTF16_LE, "utf-16-le"), 568c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # expat does not support utf-32 569c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren #(codecs.BOM_UTF32_BE, "utf-32-be"), 570c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren #(codecs.BOM_UTF32_LE, "utf-32-le"), 571c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren ): 572c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if not header.startswith(bom): 573c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren continue 574c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 575c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for start in prefixes: 576c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren prefix = bom + start.decode('ascii').encode(encoding) 577c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if header[:len(prefix)] == prefix: 578c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return True 579c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 580c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return False 581c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 582c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 583c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# Binary Plist 584c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 585c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 586c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 587c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenclass InvalidFileException (ValueError): 588c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def __init__(self, message="Invalid file"): 589c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren ValueError.__init__(self, message) 590c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 591c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren_BINARY_FORMAT = {1: 'B', 2: 'H', 4: 'L', 8: 'Q'} 592c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 593c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenclass _BinaryPlistParser: 594c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 595c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren Read or write a binary plist file, following the description of the binary 596c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren format. Raise InvalidFileException in case of error, otherwise return the 597c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren root object. 598c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 599c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren see also: http://opensource.apple.com/source/CF/CF-744.18/CFBinaryPList.c 600c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 601c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def __init__(self, use_builtin_types, dict_type): 602c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._use_builtin_types = use_builtin_types 603c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._dict_type = dict_type 604c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 605c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def parse(self, fp): 606c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren try: 607c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # The basic file format: 608c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # HEADER 609c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # object... 610c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # refid->offset... 611c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # TRAILER 612c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp = fp 613c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.seek(-32, os.SEEK_END) 614c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren trailer = self._fp.read(32) 615c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if len(trailer) != 32: 616c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren raise InvalidFileException() 617c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren ( 618c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren offset_size, self._ref_size, num_objects, top_object, 619c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren offset_table_offset 620c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren ) = struct.unpack('>6xBBQQQ', trailer) 621c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.seek(offset_table_offset) 622065266450ea5519a43bcc199e48d304f1e7038e8Serhiy Storchaka self._object_offsets = self._read_ints(num_objects, offset_size) 623c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return self._read_object(self._object_offsets[top_object]) 624c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 625c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren except (OSError, IndexError, struct.error): 626c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren raise InvalidFileException() 627c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 628c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def _get_size(self, tokenL): 629c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ return the size of the next object.""" 630c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if tokenL == 0xF: 631c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren m = self._fp.read(1)[0] & 0x3 632c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren s = 1 << m 633c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren f = '>' + _BINARY_FORMAT[s] 634c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return struct.unpack(f, self._fp.read(s))[0] 635c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 636c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return tokenL 637c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 638065266450ea5519a43bcc199e48d304f1e7038e8Serhiy Storchaka def _read_ints(self, n, size): 639065266450ea5519a43bcc199e48d304f1e7038e8Serhiy Storchaka data = self._fp.read(size * n) 640065266450ea5519a43bcc199e48d304f1e7038e8Serhiy Storchaka if size in _BINARY_FORMAT: 641065266450ea5519a43bcc199e48d304f1e7038e8Serhiy Storchaka return struct.unpack('>' + _BINARY_FORMAT[size] * n, data) 642065266450ea5519a43bcc199e48d304f1e7038e8Serhiy Storchaka else: 643065266450ea5519a43bcc199e48d304f1e7038e8Serhiy Storchaka return tuple(int.from_bytes(data[i: i + size], 'big') 644065266450ea5519a43bcc199e48d304f1e7038e8Serhiy Storchaka for i in range(0, size * n, size)) 645065266450ea5519a43bcc199e48d304f1e7038e8Serhiy Storchaka 646c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def _read_refs(self, n): 647065266450ea5519a43bcc199e48d304f1e7038e8Serhiy Storchaka return self._read_ints(n, self._ref_size) 648c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 649c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def _read_object(self, offset): 650c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 651c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren read the object at offset. 652c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 653c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren May recursively read sub-objects (content of an array/dict/set) 654c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 655c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.seek(offset) 656c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren token = self._fp.read(1)[0] 657c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren tokenH, tokenL = token & 0xF0, token & 0x0F 658c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 659c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if token == 0x00: 660c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return None 661c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 662c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif token == 0x08: 663c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return False 664c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 665c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif token == 0x09: 666c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return True 667c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 668c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # The referenced source code also mentions URL (0x0c, 0x0d) and 669c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # UUID (0x0e), but neither can be generated using the Cocoa libraries. 670c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 671c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif token == 0x0f: 672c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return b'' 673c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 674c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif tokenH == 0x10: # int 6756db6653bbc0841600248e4d7b7542591067c4157Ronald Oussoren return int.from_bytes(self._fp.read(1 << tokenL), 6766db6653bbc0841600248e4d7b7542591067c4157Ronald Oussoren 'big', signed=tokenL >= 3) 677c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 678c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif token == 0x22: # real 679c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return struct.unpack('>f', self._fp.read(4))[0] 680c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 681c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif token == 0x23: # real 682c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return struct.unpack('>d', self._fp.read(8))[0] 683c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 684c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif token == 0x33: # date 685c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren f = struct.unpack('>d', self._fp.read(8))[0] 686c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # timestamp 0 of binary plists corresponds to 1/1/2001 687c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # (year of Mac OS X 10.0), instead of 1/1/1970. 68894ad49fabc15c2eaafb5b590701ceb642d56bec0Serhiy Storchaka return datetime.datetime(2001, 1, 1) + datetime.timedelta(seconds=f) 689c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 690c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif tokenH == 0x40: # data 691c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren s = self._get_size(tokenL) 692c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if self._use_builtin_types: 693c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return self._fp.read(s) 694c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 695c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return Data(self._fp.read(s)) 696c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 697c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif tokenH == 0x50: # ascii string 698c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren s = self._get_size(tokenL) 699c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren result = self._fp.read(s).decode('ascii') 700c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return result 701c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 702c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif tokenH == 0x60: # unicode string 703c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren s = self._get_size(tokenL) 704c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return self._fp.read(s * 2).decode('utf-16be') 705c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 706c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # tokenH == 0x80 is documented as 'UID' and appears to be used for 707c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # keyed-archiving, not in plists. 708c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 709c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif tokenH == 0xA0: # array 710c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren s = self._get_size(tokenL) 711c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren obj_refs = self._read_refs(s) 712c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return [self._read_object(self._object_offsets[x]) 713c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for x in obj_refs] 714c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 715c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # tokenH == 0xB0 is documented as 'ordset', but is not actually 716c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # implemented in the Apple reference code. 717c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 718c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # tokenH == 0xC0 is documented as 'set', but sets cannot be used in 719c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # plists. 720c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 721c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif tokenH == 0xD0: # dict 722c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren s = self._get_size(tokenL) 723c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren key_refs = self._read_refs(s) 724c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren obj_refs = self._read_refs(s) 725c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren result = self._dict_type() 726c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for k, o in zip(key_refs, obj_refs): 727c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren result[self._read_object(self._object_offsets[k]) 728c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren ] = self._read_object(self._object_offsets[o]) 729c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return result 730c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 731c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren raise InvalidFileException() 732c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 733c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef _count_to_size(count): 734c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if count < 1 << 8: 735c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return 1 736c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 737c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif count < 1 << 16: 738c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return 2 739c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 740c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif count << 1 << 32: 741c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return 4 742c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 743c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 744c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return 8 745c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 746c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorenclass _BinaryPlistWriter (object): 747c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def __init__(self, fp, sort_keys, skipkeys): 748c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp = fp 749c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._sort_keys = sort_keys 750c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._skipkeys = skipkeys 751c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 752c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def write(self, value): 753c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 754c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # Flattened object list: 755c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._objlist = [] 756c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 757c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # Mappings from object->objectid 758c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # First dict has (type(object), object) as the key, 759c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # second dict is used when object is not hashable and 760c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # has id(object) as the key. 761c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._objtable = {} 762c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._objidtable = {} 763c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 764c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # Create list of all objects in the plist 765c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._flatten(value) 766c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 767c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # Size of object references in serialized containers 768c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # depends on the number of objects in the plist. 769c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren num_objects = len(self._objlist) 770c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._object_offsets = [0]*num_objects 771c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._ref_size = _count_to_size(num_objects) 772c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 773c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._ref_format = _BINARY_FORMAT[self._ref_size] 774c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 775c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # Write file header 776c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(b'bplist00') 777c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 778c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # Write object list 779c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for obj in self._objlist: 780c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._write_object(obj) 781c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 782c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # Write refnum->object offset table 783c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren top_object = self._getrefnum(value) 784c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren offset_table_offset = self._fp.tell() 785c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren offset_size = _count_to_size(offset_table_offset) 786c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren offset_format = '>' + _BINARY_FORMAT[offset_size] * num_objects 787c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack(offset_format, *self._object_offsets)) 788c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 789c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # Write trailer 790c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren sort_version = 0 791c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren trailer = ( 792c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren sort_version, offset_size, self._ref_size, num_objects, 793c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren top_object, offset_table_offset 794c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren ) 795c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>5xBBBQQQ', *trailer)) 796c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 797c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def _flatten(self, value): 798c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # First check if the object is in the object table, not used for 799c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # containers to ensure that two subcontainers with the same contents 800c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # will be serialized as distinct values. 801c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if isinstance(value, ( 802c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren str, int, float, datetime.datetime, bytes, bytearray)): 803c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if (type(value), value) in self._objtable: 804c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return 805c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 806c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, Data): 807c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if (type(value.data), value.data) in self._objtable: 808c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return 809c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 810c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # Add to objectreference map 811c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren refnum = len(self._objlist) 812c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._objlist.append(value) 813c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren try: 814c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if isinstance(value, Data): 815c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._objtable[(type(value.data), value.data)] = refnum 816c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 817c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._objtable[(type(value), value)] = refnum 818c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren except TypeError: 819c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._objidtable[id(value)] = refnum 820c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 821c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren # And finally recurse into containers 822c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if isinstance(value, dict): 823c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren keys = [] 824c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren values = [] 825c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren items = value.items() 826c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if self._sort_keys: 827c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren items = sorted(items) 828c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 829c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for k, v in items: 830c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if not isinstance(k, str): 831c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if self._skipkeys: 832c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren continue 833c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren raise TypeError("keys must be strings") 834c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren keys.append(k) 835c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren values.append(v) 836c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 837c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for o in itertools.chain(keys, values): 838c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._flatten(o) 839c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 840c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, (list, tuple)): 841c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for o in value: 842c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._flatten(o) 843c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 844c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def _getrefnum(self, value): 845c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren try: 846c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if isinstance(value, Data): 847c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return self._objtable[(type(value.data), value.data)] 848c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 849c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return self._objtable[(type(value), value)] 850c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren except TypeError: 851c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return self._objidtable[id(value)] 852c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 853c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def _write_size(self, token, size): 854c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if size < 15: 855c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>B', token | size)) 856c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 857c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif size < 1 << 8: 858c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>BBB', token | 0xF, 0x10, size)) 859c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 860c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif size < 1 << 16: 861c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>BBH', token | 0xF, 0x11, size)) 862c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 863c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif size < 1 << 32: 864c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>BBL', token | 0xF, 0x12, size)) 865c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 866c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 867c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>BBQ', token | 0xF, 0x13, size)) 868c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 869c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren def _write_object(self, value): 870c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren ref = self._getrefnum(value) 871c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._object_offsets[ref] = self._fp.tell() 872c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if value is None: 873c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(b'\x00') 874c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 875c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif value is False: 876c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(b'\x08') 877c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 878c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif value is True: 879c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(b'\x09') 880c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 881c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, int): 8826db6653bbc0841600248e4d7b7542591067c4157Ronald Oussoren if value < 0: 8836db6653bbc0841600248e4d7b7542591067c4157Ronald Oussoren try: 8846db6653bbc0841600248e4d7b7542591067c4157Ronald Oussoren self._fp.write(struct.pack('>Bq', 0x13, value)) 8856db6653bbc0841600248e4d7b7542591067c4157Ronald Oussoren except struct.error: 88694e44a935b3dc1e67a6a3357f64324ee0c81d40cRonald Oussoren raise OverflowError(value) from None 8876db6653bbc0841600248e4d7b7542591067c4157Ronald Oussoren elif value < 1 << 8: 888c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>BB', 0x10, value)) 889c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif value < 1 << 16: 890c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>BH', 0x11, value)) 891c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif value < 1 << 32: 892c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>BL', 0x12, value)) 89394e44a935b3dc1e67a6a3357f64324ee0c81d40cRonald Oussoren elif value < 1 << 63: 89494e44a935b3dc1e67a6a3357f64324ee0c81d40cRonald Oussoren self._fp.write(struct.pack('>BQ', 0x13, value)) 89594e44a935b3dc1e67a6a3357f64324ee0c81d40cRonald Oussoren elif value < 1 << 64: 89694e44a935b3dc1e67a6a3357f64324ee0c81d40cRonald Oussoren self._fp.write(b'\x14' + value.to_bytes(16, 'big', signed=True)) 897c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 89894e44a935b3dc1e67a6a3357f64324ee0c81d40cRonald Oussoren raise OverflowError(value) 899c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 900c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, float): 901c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>Bd', 0x23, value)) 902c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 903c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, datetime.datetime): 904c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren f = (value - datetime.datetime(2001, 1, 1)).total_seconds() 905c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>Bd', 0x33, f)) 906c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 907c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, Data): 908c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._write_size(0x40, len(value.data)) 909c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(value.data) 910c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 911c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, (bytes, bytearray)): 912c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._write_size(0x40, len(value)) 913c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(value) 914c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 915c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, str): 916c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren try: 917c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren t = value.encode('ascii') 918c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._write_size(0x50, len(value)) 919c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren except UnicodeEncodeError: 920c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren t = value.encode('utf-16be') 9217338ebc4ba00366fa2c4f592630c2acd98c178d8Serhiy Storchaka self._write_size(0x60, len(t) // 2) 922c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 923c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(t) 924c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 925c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, (list, tuple)): 926c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren refs = [self._getrefnum(o) for o in value] 927c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren s = len(refs) 928c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._write_size(0xA0, s) 929c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>' + self._ref_format * s, *refs)) 930c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 931c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren elif isinstance(value, dict): 932c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren keyRefs, valRefs = [], [] 933c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 934c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if self._sort_keys: 935c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren rootItems = sorted(value.items()) 936c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 937c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren rootItems = value.items() 938c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 939c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for k, v in rootItems: 940c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if not isinstance(k, str): 941c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if self._skipkeys: 942c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren continue 943c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren raise TypeError("keys must be strings") 944c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren keyRefs.append(self._getrefnum(k)) 945c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren valRefs.append(self._getrefnum(v)) 946c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 947c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren s = len(keyRefs) 948c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._write_size(0xD0, s) 949c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>' + self._ref_format * s, *keyRefs)) 950c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren self._fp.write(struct.pack('>' + self._ref_format * s, *valRefs)) 951c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 952c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 9536db6653bbc0841600248e4d7b7542591067c4157Ronald Oussoren raise TypeError(value) 954c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 955c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 956c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef _is_fmt_binary(header): 957c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return header[:8] == b'bplist00' 958c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 959c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 960c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 961c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# Generic bits 962c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren# 963c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 964c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren_FORMATS={ 965c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren FMT_XML: dict( 966c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren detect=_is_fmt_xml, 967c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren parser=_PlistParser, 968c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren writer=_PlistWriter, 969c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren ), 970c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren FMT_BINARY: dict( 971c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren detect=_is_fmt_binary, 972c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren parser=_BinaryPlistParser, 973c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren writer=_BinaryPlistWriter, 974c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren ) 975c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren} 976c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 977c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 978c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef load(fp, *, fmt=None, use_builtin_types=True, dict_type=dict): 979c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """Read a .plist file. 'fp' should be (readable) file object. 980c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren Return the unpacked root object (which usually is a dictionary). 981c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 982c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if fmt is None: 983c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren header = fp.read(32) 984c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren fp.seek(0) 985c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren for info in _FORMATS.values(): 986c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if info['detect'](header): 9878966759b031ce9977e038c5db1d8ed47c6c827a6Serhiy Storchaka P = info['parser'] 988c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren break 989c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 990c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 991c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren raise InvalidFileException() 992c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 993c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren else: 9948966759b031ce9977e038c5db1d8ed47c6c827a6Serhiy Storchaka P = _FORMATS[fmt]['parser'] 995c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 9968966759b031ce9977e038c5db1d8ed47c6c827a6Serhiy Storchaka p = P(use_builtin_types=use_builtin_types, dict_type=dict_type) 997c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return p.parse(fp) 998c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 999c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 1000c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef loads(value, *, fmt=None, use_builtin_types=True, dict_type=dict): 1001c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """Read a .plist file from a bytes object. 1002c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren Return the unpacked root object (which usually is a dictionary). 1003c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 1004c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren fp = BytesIO(value) 1005c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return load( 1006c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren fp, fmt=fmt, use_builtin_types=use_builtin_types, dict_type=dict_type) 1007c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 1008c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 1009c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef dump(value, fp, *, fmt=FMT_XML, sort_keys=True, skipkeys=False): 1010c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """Write 'value' to a .plist file. 'fp' should be a (writable) 1011c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren file object. 1012c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 1013c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren if fmt not in _FORMATS: 1014c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren raise ValueError("Unsupported format: %r"%(fmt,)) 1015c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 1016c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren writer = _FORMATS[fmt]["writer"](fp, sort_keys=sort_keys, skipkeys=skipkeys) 1017c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren writer.write(value) 1018c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 1019c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren 1020c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussorendef dumps(value, *, fmt=FMT_XML, skipkeys=False, sort_keys=True): 1021c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """Return a bytes object with the contents for a .plist file. 1022c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren """ 1023c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren fp = BytesIO() 1024c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren dump(value, fp, fmt=fmt, skipkeys=skipkeys, sort_keys=sort_keys) 1025c5cf7973422dce0ed59849aaf2d708d4c6b7320dRonald Oussoren return fp.getvalue() 1026