├── LICENSE ├── plistsubtractor.py └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Joshua Wright 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /plistsubtractor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) Joshua Wright 2015'ish 3 | 4 | import sys 5 | import os 6 | try: 7 | import biplist 8 | except ImportError: 9 | sys.stdout.write("You must install the biplist module: pip install biplist\n") 10 | sys.exit(1) 11 | 12 | def extplist(plistfile): 13 | #print "Processing " + plistfile 14 | try: 15 | plistelements=biplist.readPlist(plistfile) 16 | except biplist.InvalidPlistException: 17 | print "Invalid plist file: " + plistfile 18 | return 19 | except OverflowError: 20 | print "Invalid plist data: " + plistfile 21 | return 22 | except IOError: 23 | print "Bad file name: " + plistfile 24 | return 25 | 26 | # Sometimes we get a list or string, not a dictionary 27 | if not isinstance(plistelements, dict): 28 | return 29 | 30 | for key in plistelements: 31 | if isinstance(plistelements[key], biplist.Data): 32 | # Try to extract plist 33 | try: 34 | newp=biplist.readPlistFromString(plistelements[key]) 35 | except biplist.InvalidPlistException: 36 | # Not valid plist data 37 | continue 38 | 39 | newfilename=os.path.basename(plistfile[:-6]) + "-" + key + ".plist" 40 | i=1 41 | while True: 42 | if not os.path.exists(newfilename): 43 | break 44 | else: 45 | newfilename=os.path.basename(plistfile[:-6]) + "-" + key + "-" + str(i) + ".plist" 46 | i+=1 47 | print "Writing " + newfilename 48 | try: 49 | biplist.writePlist(newp,newfilename) 50 | except: 51 | print "Could not write plist file " + newfilename 52 | print sys.exc_info()[0] 53 | extplist(newfilename) 54 | 55 | if __name__ == "__main__": 56 | if len(sys.argv) == 1: 57 | print "Usage: %s [plist files]"%sys.argv[0] 58 | sys.exit(0) 59 | for pfile in sys.argv[1:]: 60 | if pfile[-6:] != ".plist": 61 | print "Skipping file '%s'. File names must end in '.plist'"%pfile 62 | continue 63 | extplist(pfile) 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # plistsubtractor 2 | Read a plist file, write out any embedded plist files. 3 | 4 | ## Output Files 5 | Embedded plist files are written with the same filename up to the ```.plist``` filename extension, adding the key name representing the embedded plist data at the end of the filename (e.g. the embedded plist data in the key ```dndEffectiveOverrides``` in the ```com.apple.nano.plist``` file will produce a new file called ```com.apple.nano-dndEffectiveOverrides.plist```. If the filename already exists, a sequential identifier is added to the filename (```-1```, ```-2```, ```-3```, etc.) Output files are written in the current working directory when the plistsubtractor script is run. 6 | 7 | ## Examples 8 | 9 | ``` 10 | $ ls com.apple.nano.plist 11 | com.apple.nano.plist 12 | $ plistsubtractor.py com.apple.nano.plist 13 | Writing com.apple.nano-dndEffectiveOverrides.plist 14 | $ ls -l com.apple.nano* 15 | -rw-r--r-- 1 jwright staff 764 Dec 26 19:35 com.apple.nano-dndEffectiveOverrides.plist 16 | -rw-r--r-- 1 jwright staff 1046 Dec 26 16:21 com.apple.nano.plist 17 | ``` 18 | 19 | You can specify multiple filenames in one command: 20 | 21 | ``` 22 | $ ls 23 | com.apple.Accessibility.plist com.apple.NanoMail.plist com.apple.companionappd.plist com.apple.nano.plist 24 | com.apple.Carousel.plist com.apple.NanoMusicSync.plist com.apple.healthd.plist com.apple.nanopassbook.plist 25 | com.apple.ET.plist com.apple.ToneLibrary.plist com.apple.mobilecal.plist com.apple.nanosystemsettings.plist 26 | com.apple.MobileSMS.plist com.apple.bulletinboard.apps.plist com.apple.mobilephone.plist com.apple.stockholm.plist 27 | $ plistsubtractor.py *.plist 28 | Writing com.apple.Carousel-IconPositions.plist 29 | Writing com.apple.NanoMail-NanoMailIncludeMail.plist 30 | Writing com.apple.mobilephone-kVoicemailForReplicationKey.plist 31 | Writing com.apple.nano-dndEffectiveOverrides.plist 32 | ``` 33 | 34 | Combined with find, whee! 35 | 36 | ``` 37 | $ find ~/Library/ -type f -name \*.plist -print0 | xargs -0 plistsubtractor.py 38 | Writing Saved Status-Saved Status Array.plist 39 | Writing Info-iBooks Data 2.plist 40 | Writing Info-iBooks Data 2-1.plist 41 | Writing Info-iBooks Data 2-2.plist 42 | Writing Info-iBooks Data 2-3.plist 43 | Writing Info-iBooks Data 2-4.plist 44 | Writing Info-iBooks Data 2-5.plist 45 | Writing Info-iBooks Data 2-6.plist 46 | Writing Info-iBooks Data 2-7.plist 47 | Writing Saved Status-Saved Status Array-1.plist 48 | Writing com.apple.commerce.knownclients-com.apple.appstore:453.plist 49 | Writing com.apple.commerce.knownclients-com.apple.appstore:376.plist 50 | Writing com.apple.commerce.knownclients-com.apple.appstore:459.plist 51 | Writing com.apple.commerce.knownclients-com.apple.ibooks:453.plist 52 | Writing com.apple.commerce.knownclients-com.apple.ibooks:376.plist 53 | Writing Info-FFSegmentStoreStoredFrameIndexes.plist 54 | Writing Info-FFSegmentStoreStoredFrameIndexes-1.plist 55 | Writing Info-FFSegmentStoreStoredFrameIndexes-2.plist 56 | ``` 57 | 58 | ## Questions? 59 | 60 | Joshua Wright, @joswr1ght, jwright@hasborg.com 61 | --------------------------------------------------------------------------------