from base64 import b64encode, b64decode from struct import pack, unpack class PlistWriter: def __init__(self): self.parts = [] self.indention = 0 def emit(self, s): self.parts.append(s) def finish(self): result = '\n'.join(self.parts) self.parts = [] self.indention = 0 return result class NSArchiver: def __init__(self): self.map = {} self.writer = PlistWriter() self.refs = [] def _archive(self, val): if isinstance(val, Ref): idx = None if val in self.map: idx = self.map[val] else: idx = len(self.refs) self.map[val] = idx self.refs.append(val) self.writer.emit('') self.writer.emit('CF$UID') self.writer.emit('{}'.format(idx)) self.writer.emit('') elif val is True: self.writer.emit('') elif val is False: self.writer.emit('') elif isinstance(val, str): self.writer.emit('{}'.format(val)) elif isinstance(val, bytes): b64 = b64encode(val).decode('ascii') self.writer.emit('{}'.format(b64)) elif isinstance(val, int): self.writer.emit('{}'.format(val)) elif isinstance(val, float): self.writer.emit('{}'.format(val)) elif isinstance(val, list): self.writer.emit('') for e in val: self._archive(e) self.writer.emit('') elif isinstance(val, dict): self.writer.emit('') for k, v in val.items(): assert(isinstance(k, str)) self.writer.emit('{}'.format(k)) self._archive(v) self.writer.emit('') else: raise Exception("Cannot serialize value of type {}".format(type(val))) def archive(self, val): assert(isinstance(val, Ref)) self.writer.emit('') self.writer.emit('') self.writer.emit('') self.writer.emit('') self.writer.emit(' $archiver') self.writer.emit(' NSKeyedArchiver') self.writer.emit(' $objects') self.writer.emit(' ') idx = 0 self.refs = [null, val] while idx < len(self.refs): r = self.refs[idx] self.map[r] = idx self._archive(r.v) idx += 1 self.writer.emit('') self.writer.emit('$top') self.writer.emit('') self.writer.emit('root') self.writer.emit('') self.writer.emit('CF$UID') self.writer.emit('1') self.writer.emit('') self.writer.emit('') self.writer.emit('$version') self.writer.emit('100000') self.writer.emit('') self.writer.emit('') return self.writer.finish() class Ref: def __init__(self, v): assert(not isinstance(v, Ref)) self.v = v def __hash__(self): return id(self) def __cmp__(self, other): return id(self) == id(other) def __str__(self): return "ref({})".format(self.v) # Converts the agument into a reference type. def ref(obj): return Ref(obj) def cls(hierarchy): return ref({ "$classes": hierarchy, "$classname": hierarchy[0] }) def nsstring(content): return ref({ '$class': nsstring_class, 'NS.string': content, }) def nsmutablestring(content): return ref({ '$class': nsmutablestring_class, 'NS.string': content, }) def nsdictionary(vals): return ref({ '$class': nsdictionary_class, 'NS.keys': list(vals.keys()), 'NS.objects': list(vals.values()), }) def nsdata(content): return ref({ '$class': nsdata_class, 'NS.data': content, }) def nsmutabledata(content): return ref({ '$class': nsmutabledata_class, 'NS.data': content, }) def nsarray(vals): return ref({ '$class': nsarray_class, 'NS.objects': vals }) def nsmutarray(vals): return ref({ '$class': nsmutarray_class, 'NS.objects': vals }) def old_style_array(vals, typeid, elemsize): d = { '$class': nskeyed_coder_old_style_array_class, 'NS.count': len(vals), 'NS.size': elemsize, 'NS.type': typeid } for i, v in enumerate(vals): d['${}'.format(i)] = v return ref(d) null = ref("$null") nsstring_class = cls(["NSString", "NSObject"]) nsmutablestring_class = cls(["NSMutableString", "NSString", "NSObject"]) ac_zeroing_string = cls(["ACZeroingString", "NSString", "NSObject"]) nsdata_class = cls(["NSData", "NSObject"]) nsmutabledata_class = cls(["NSMutableData", "NSData", "NSObject"]) nsvalue_class = cls(["NSValue", "NSObject"]) nskeyed_coder_old_style_array_class = cls(["_NSKeyedCoderOldStyleArray", "NSObject"]) nsarray_class = cls(["NSArray", "NSObject"]) nsdictionary_class = cls(["NSDictionary", "NSObject"]) nsmutarray_class = cls(["NSMutableArray", "NSArray", "NSObject"]) pfarray_class = cls(["_PFArray", "NSArray", "NSObject"]) shared_key_dict_class = cls(["NSSharedKeyDictionary", "NSMutableDictionary", "NSDictionary", "NSObject"]) shared_key_set_class = cls(["NSSharedKeySet", "NSObject"]) cs_localized_string_class = cls(["CSLocalizedString", "NSString", "NSObject"]) ns_localized_string_class = cls(["__NSLocalizedString", "NSMutableString", "NSString", "NSObject"])