├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── Sources ├── NSData+HexString.h ├── NSData+HexString.m ├── defaults.h ├── defaults.m ├── helpers.m └── write.m └── ent.plist /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | push: 4 | paths: 5 | - '*.m' 6 | - '*.h' 7 | - '.github/workflows/*' 8 | - 'Makefile' 9 | pull_request: 10 | paths: 11 | - '*.c' 12 | - '*.cpp' 13 | - '.github/workflows/*' 14 | - 'Makefile' 15 | workflow_dispatch: 16 | release: 17 | types: 18 | - created 19 | 20 | jobs: 21 | build-macos: 22 | runs-on: macos-11 23 | strategy: 24 | matrix: 25 | target: 26 | - iphoneos 27 | - watchos 28 | - appletvos 29 | env: 30 | TARGET: ${{ matrix.target }} 31 | steps: 32 | - uses: actions/checkout@v1 33 | with: 34 | submodules: recursive 35 | 36 | - name: setup environment 37 | run: | 38 | if [ "${TARGET}" = "watchos" ]; then 39 | ARCH="arm64_32" 40 | else 41 | ARCH="arm64" 42 | fi 43 | echo "CC=xcrun -sdk ${TARGET} cc -arch ${ARCH}" >> $GITHUB_ENV 44 | echo "STRIP=xcrun -sdk ${TARGET} strip" >> $GITHUB_ENV 45 | echo "CFLAGS=-Os -flto=thin -m${TARGET}-version-min=7.0" >> $GITHUB_ENV 46 | echo "LDFLAGS=-Os -flto=thin -m${TARGET}-version-min=7.0" >> $GITHUB_ENV 47 | gh run download -R ProcursusTeam/ldid -n ldid_macos_x86_64 48 | chmod +x ldid 49 | echo "LDID=./ldid" >> $GITHUB_ENV 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | 53 | - name: build 54 | run: | 55 | make -j$(sysctl -n hw.ncpu) 56 | 57 | - uses: actions/upload-artifact@v1 58 | with: 59 | name: defaults_${{ matrix.target }} 60 | path: defaults 61 | 62 | - name: Upload Release Asset 63 | uses: actions/upload-release-asset@v1 64 | if: ${{ github.event_name == 'release' }} 65 | env: 66 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 67 | with: 68 | upload_url: ${{ github.event.release.upload_url }} 69 | asset_path: defaults 70 | asset_name: defaults_${{ matrix.target }} 71 | asset_content_type: application/octet-stream 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | .DS_Store 3 | defaults 4 | *.o 5 | compile_commands.json 6 | .cache 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-present quiprr 2 | Modified work Copyright (c) 2021 ProcursusTeam 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC ?= xcrun cc 2 | STRIP ?= xcrun strip 3 | CFLAGS ?= -O2 4 | LDFLAGS ?= -O2 5 | 6 | LDID ?= ldid 7 | 8 | SRC := Sources/defaults.m Sources/write.m 9 | SRC += Sources/helpers.m 10 | SRC += Sources/NSData+HexString.m 11 | 12 | all: defaults 13 | 14 | defaults: $(SRC:%=%.o) 15 | $(CC) $(LDFLAGS) -o $@ $^ -framework CoreFoundation -framework MobileCoreServices -fobjc-arc 16 | $(STRIP) $@ 17 | -$(LDID) -Sent.plist $@ 18 | 19 | %.m.o: %.m 20 | $(CC) $(CFLAGS) -c -o $@ $< -fobjc-arc 21 | 22 | clean: 23 | rm -rf defaults defaults.dSYM $(SRC:%=%.o) 24 | 25 | .PHONY: clean all 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # defaults 2 | Opensource re-implementation of `defaults(1)` 3 | 4 | ### Additions: 5 | * `-container` - The official `defaults(1)` does not have appropriate support for containers. 6 | 7 | ### Help: 8 | ``` 9 | Command line interface to a user's defaults. 10 | Syntax: 11 | 12 | 'defaults' [-currentHost | -host ] followed by one of the following: 13 | 14 | read shows all defaults 15 | read shows defaults for given domain 16 | read shows defaults for given domain, key 17 | 18 | read-type shows the type for the given domain, key 19 | 20 | write writes domain (overwrites existing) 21 | write writes key for domain 22 | 23 | rename renames old_key to new_key 24 | 25 | delete deletes domain 26 | delete deletes key in domain 27 | 28 | import writes the plist at path to domain 29 | import - writes a plist from stdin to domain 30 | export saves domain as a binary plist to path 31 | export - writes domain as an xml plist to stdout 32 | domains lists all domains 33 | find lists all entries containing word 34 | help print this help 35 | 36 | is ( | -app | -globalDomain ) 37 | or a path to a file omitting the '.plist' extension 38 | 39 | [-container ( | | )] 40 | may be specified before the domain name to change the container 41 | this is a Procursus extension 42 | 43 | is one of: 44 | 45 | -string 46 | -data 47 | -int[eger] 48 | -float 49 | -bool[ean] (true | false | yes | no) 50 | -date 51 | -array ... 52 | -array-add ... 53 | -dict ... 54 | -dict-add ... 55 | 56 | Contact the Procursus Team for support. 57 | ``` 58 | -------------------------------------------------------------------------------- /Sources/NSData+HexString.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface NSData (HexString) 4 | + (id)dataWithHexString:(NSString *)hex; 5 | @end 6 | -------------------------------------------------------------------------------- /Sources/NSData+HexString.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+HexString.m 3 | // libsecurity_transform 4 | // 5 | // Copyright (c) 2011 Apple, Inc. All rights reserved. 6 | // 7 | 8 | #import "NSData+HexString.h" 9 | 10 | @implementation NSData (HexString) 11 | 12 | // Not efficent 13 | +(id)dataWithHexString:(NSString *)hex 14 | { 15 | char buf[3]; 16 | buf[2] = '\0'; 17 | NSAssert(0 == [hex length] % 2, @"Hex strings should have an even number of digits (%@)", hex); 18 | unsigned char *bytes = malloc([hex length]/2); 19 | unsigned char *bp = bytes; 20 | for (CFIndex i = 0; i < [hex length]; i += 2) { 21 | buf[0] = [hex characterAtIndex:i]; 22 | buf[1] = [hex characterAtIndex:i+1]; 23 | char *b2 = NULL; 24 | *bp++ = strtol(buf, &b2, 16); 25 | NSAssert(b2 == buf + 2, @"String should be all hex digits: %@ (bad digit around %ld)", hex, (long)i); 26 | } 27 | 28 | return [NSData dataWithBytesNoCopy:bytes length:[hex length]/2 freeWhenDone:YES]; 29 | } 30 | 31 | /* Override the new NSData description to get only hex byes */ 32 | - (NSString*)description { 33 | return [self debugDescription]; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Sources/defaults.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | * 4 | * Copyright (c) 2021 ProcursusTeam 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #import 26 | #import 27 | 28 | int defaultsWrite(NSArray*, NSString*, CFStringRef, CFStringRef); 29 | void usage(void); 30 | CFPropertyListRef parseTypedArg(NSString*, NSString*, bool); 31 | NSObject* parsePropertyList(NSString*); 32 | bool isType(NSString*); 33 | NSArray *flatten(NSObject*); 34 | NSString *prettyName(NSString *); 35 | 36 | /* 37 | * NSDateFormatter doesn't seem to work correctly, 38 | * so we use these deprecated methods that are used in Apple's defaults. 39 | */ 40 | @interface NSDate (deprecated) 41 | + (id)dateWithNaturalLanguageString:(NSString *)string; 42 | + (id)dateWithString:(NSString *)aString; 43 | @end 44 | 45 | @interface LSApplicationProxy : NSObject 46 | @property(nonatomic, assign, readonly) NSString *applicationIdentifier; 47 | + (id)applicationProxyForIdentifier:(id)arg1; 48 | - (NSURL *)containerURL; 49 | - (id)localizedNameForContext:(id)context; 50 | @end 51 | 52 | @interface LSApplicationWorkspace : NSObject 53 | + (id)defaultWorkspace; 54 | - (NSArray *)allInstalledApplications; 55 | @end 56 | 57 | CFArrayRef _CFPreferencesCopyKeyListWithContainer(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef container); 58 | CFDictionaryRef _CFPreferencesCopyMultipleWithContainer(CFArrayRef keysToFetch, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef container); 59 | CFPropertyListRef _CFPreferencesCopyValueWithContainer(CFStringRef key, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef container); 60 | void _CFPreferencesSetMultipleWithContainer(CFDictionaryRef keysToSet, CFArrayRef keysToRemove, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef container); 61 | void _CFPreferencesSetValueWithContainer(CFStringRef key, CFPropertyListRef value, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef container); 62 | Boolean _CFPreferencesSynchronizeWithContainer(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef container); 63 | void _CFPrefsSetSynchronizeIsSynchronous(int); 64 | void _CFPrefsSynchronizeForProcessTermination(void); 65 | void _CFPrefSetInvalidPropertyListDeletionEnabled(int); 66 | -------------------------------------------------------------------------------- /Sources/defaults.m: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | * 4 | * Copyright (c) 2020-present quiprr 5 | * Modified work Copyright (c) 2021 ProcursusTeam 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | const unsigned char defaultsVersionString[] = "@(#)PROGRAM:defaults PROJECT:defaults-1.0.1\n"; 27 | 28 | #import 29 | #import 30 | #include 31 | 32 | #include "defaults.h" 33 | 34 | int main(int argc, char *argv[], char *envp[]) 35 | { 36 | @autoreleasepool { 37 | NSMutableArray *args = [[[NSProcessInfo processInfo] arguments] mutableCopy]; 38 | 39 | CFStringRef host = kCFPreferencesAnyHost; 40 | if (args.count >= 2 && [args[1] isEqualToString:@"-currentHost"]) { 41 | host = kCFPreferencesCurrentHost; 42 | [args removeObjectAtIndex:1]; 43 | } else if (args.count >= 3 && [args[1] isEqualToString:@"-host"]) { 44 | host = (__bridge CFStringRef)args[2]; 45 | [args removeObjectAtIndex:2]; 46 | [args removeObjectAtIndex:1]; 47 | } 48 | 49 | if (args.count == 1) { 50 | usage(); 51 | return 255; 52 | } 53 | 54 | _CFPrefsSetSynchronizeIsSynchronous(1); 55 | _CFPrefSetInvalidPropertyListDeletionEnabled(0); 56 | 57 | CFStringRef container = CFSTR("kCFPreferencesNoContainer"); 58 | 59 | if (args.count >= 4 && [args[2] isEqualToString:@"-container"]) { 60 | if ([args[3] hasPrefix:@"/"]) { 61 | container = (__bridge CFStringRef)[args[3] stringByResolvingSymlinksInPath]; 62 | } else if ([args[3] hasPrefix:@"group."]) { 63 | NSURL *url = [[NSFileManager defaultManager] 64 | containerURLForSecurityApplicationGroupIdentifier:args[3]]; 65 | container = (__bridge CFStringRef)[(NSURL*)[url copy] path]; 66 | } else { 67 | LSApplicationProxy *app = [LSApplicationProxy applicationProxyForIdentifier:args[3]]; 68 | container = (__bridge CFStringRef)[app.containerURL path]; 69 | } 70 | [args removeObjectAtIndex:3]; 71 | [args removeObjectAtIndex:2]; 72 | } 73 | 74 | [args replaceObjectAtIndex:1 withObject:[args[1] lowercaseString]]; 75 | 76 | if (args.count == 1 || (args.count >= 2 && [args[1] isEqualToString:@"help"])) { 77 | usage(); 78 | return args.count == 1 ? 255 : 0; 79 | } 80 | 81 | if ([args[1] isEqualToString:@"domains"]) { 82 | NSMutableArray *domains = [(__bridge_transfer NSArray*)CFPreferencesCopyApplicationList(kCFPreferencesCurrentUser, host) mutableCopy]; 83 | if (domains.count != 0) { 84 | [domains removeObjectAtIndex:[domains indexOfObject:(id)kCFPreferencesAnyApplication]]; 85 | [domains sortUsingSelector:@selector(compare:)]; 86 | } 87 | printf("%s\n", [domains componentsJoinedByString:@", "].UTF8String); 88 | return 0; 89 | } else if (args.count == 2 && [args[1] isEqualToString:@"read"]) { 90 | NSArray *prefs = (__bridge_transfer NSArray *) 91 | CFPreferencesCopyApplicationList(kCFPreferencesCurrentUser, host); 92 | NSMutableDictionary *out = [NSMutableDictionary new]; 93 | for (NSString *domain in prefs) { 94 | [out setObject:(__bridge_transfer NSDictionary*)CFPreferencesCopyMultiple(NULL, (__bridge CFStringRef)domain, kCFPreferencesCurrentUser, host) 95 | forKey:prettyName(domain)]; 96 | } 97 | printf("%s\n", [NSString stringWithFormat:@"%@", out].UTF8String); 98 | return 0; 99 | } 100 | 101 | if (args.count >= 3 && [args[1] isEqualToString:@"find"]) { 102 | NSArray *domains = (__bridge_transfer NSArray*)CFPreferencesCopyApplicationList( 103 | kCFPreferencesCurrentUser, host); 104 | long found = 0; 105 | BOOL success = false; 106 | for (NSString *domain in domains) { 107 | found = 0; 108 | if ([domain rangeOfString:args[2] options:NSCaseInsensitiveSearch].location != NSNotFound) 109 | found++; 110 | NSDictionary *dict = (__bridge_transfer NSDictionary*)CFPreferencesCopyMultiple(NULL, 111 | (__bridge CFStringRef)domain, kCFPreferencesCurrentUser, host); 112 | NSArray *flattened = flatten(dict); 113 | NSLog(@"%@", flattened); 114 | for (NSString *item in flattened) { 115 | if ([item rangeOfString:args[2] options:NSCaseInsensitiveSearch].location != NSNotFound) 116 | found++; 117 | } 118 | if (found) { 119 | success = true; 120 | printf("Found %ld keys in domain '%s': %s\n", found, 121 | prettyName(domain).UTF8String, dict.description.UTF8String); 122 | } 123 | } 124 | if (!success) 125 | NSLog(@"No domain, key, nor value containing '%@'", args[2]); 126 | return 0; 127 | } 128 | 129 | NSString *appid; 130 | 131 | if (args.count >= 3) { 132 | if ([args[2] isEqualToString:@"-g"] || [args[2] isEqualToString:@"-globalDomain"] || 133 | [args[2] isEqualToString:@"NSGlobalDomain"] || [args[2] isEqualToString:@"Apple Global Domain"]) 134 | appid = (__bridge NSString*)kCFPreferencesAnyApplication; 135 | else if (args.count >= 4 && [args[2] isEqualToString:@"-app"]) { 136 | BOOL directory; 137 | if ([[NSFileManager defaultManager] fileExistsAtPath:args[3] isDirectory:&directory] && directory) { 138 | NSBundle *appBundle = [NSBundle bundleWithPath:[args[3] stringByResolvingSymlinksInPath]]; 139 | if (appBundle == nil) { 140 | NSLog(@"Couldn't open application %@; defaults unchanged", args[3]); 141 | return 1; 142 | } 143 | appid = [appBundle bundleIdentifier]; 144 | if (appid == nil) { 145 | NSLog(@"Can't determine domain name for application %@; defaults unchanged", args[3]); 146 | return 1; 147 | } 148 | } else { 149 | LSApplicationWorkspace *workspace = [LSApplicationWorkspace defaultWorkspace]; 150 | NSArray *apps = [workspace allInstalledApplications]; 151 | for (LSApplicationProxy *proxy in apps) { 152 | if ([args[3] isEqualToString:[proxy localizedNameForContext:nil]]) { 153 | appid = proxy.applicationIdentifier; 154 | break; 155 | } 156 | } 157 | if (appid == nil) { 158 | NSLog(@"Couldn't find an application named \"%@\"; defaults unchanged", args[3]); 159 | return 1; 160 | } 161 | } 162 | [args removeObjectAtIndex:2]; 163 | } else if ([args[2] hasPrefix:@"/"]) { 164 | appid = [args[2] stringByResolvingSymlinksInPath]; 165 | } else 166 | appid = args[2]; 167 | } 168 | 169 | if ([args[1] isEqualToString:@"read"]) { 170 | NSDictionary *result = (__bridge_transfer NSDictionary *)_CFPreferencesCopyMultipleWithContainer(NULL, 171 | (__bridge CFStringRef)appid, kCFPreferencesCurrentUser, host, container); 172 | 173 | if (args.count == 3) { 174 | if ([result count] == 0) { 175 | NSLog(@"\nDomain %@ does not exist\n", appid); 176 | return 1; 177 | } 178 | printf("%s\n", result.description.UTF8String); 179 | return 0; 180 | } else { 181 | if ([result objectForKey:args[3]] == nil) { 182 | NSLog(@"\nThe domain/default pair of (%@, %@) does not exist\n", appid, args[3]); 183 | return 1; 184 | } 185 | printf("%s\n", [[result objectForKey:args[3]] description].UTF8String); 186 | return 0; 187 | } 188 | } 189 | 190 | if (args.count == 5 && [args[1] isEqualToString:@"rename"]) { 191 | CFPropertyListRef value = _CFPreferencesCopyValueWithContainer((__bridge CFStringRef)args[3], 192 | (__bridge CFStringRef)appid, kCFPreferencesCurrentUser, host, container); 193 | if (value == NULL) { 194 | NSLog(@"Key %@ does not exist in domain %@; leaving defaults unchanged", args[3], prettyName(appid)); 195 | return 1; 196 | } 197 | _CFPreferencesSetValueWithContainer((__bridge CFStringRef)args[4], value, (__bridge CFStringRef)appid, 198 | kCFPreferencesCurrentUser, host, container); 199 | _CFPreferencesSetValueWithContainer((__bridge CFStringRef)args[3], NULL, (__bridge CFStringRef)appid, 200 | kCFPreferencesCurrentUser, host, container); 201 | Boolean ret = _CFPreferencesSynchronizeWithContainer((__bridge CFStringRef)appid, 202 | kCFPreferencesCurrentUser, host, container); 203 | _CFPrefsSynchronizeForProcessTermination(); 204 | if (!ret) { 205 | NSLog(@"Failed to write domain %@", prettyName(appid)); 206 | return 1; 207 | } 208 | return 0; 209 | } 210 | 211 | if (args.count >= 4 && [args[1] isEqualToString:@"read-type"]) { 212 | CFPropertyListRef result = _CFPreferencesCopyValueWithContainer((__bridge CFStringRef)args[3], 213 | (__bridge CFStringRef)appid, kCFPreferencesCurrentUser, host, container); 214 | if (result == NULL) { 215 | NSLog(@"\nThe domain/default pair of (%@, %@) does not exist\n", appid, args[3]); 216 | return 1; 217 | } 218 | CFTypeID type = CFGetTypeID(result); 219 | if (type == CFStringGetTypeID()) { 220 | printf("Type is string\n"); 221 | } else if (type == CFDataGetTypeID()) { 222 | printf("Type is data\n"); 223 | } else if (type == CFNumberGetTypeID()) { 224 | if (CFNumberIsFloatType(result)) 225 | printf("Type is float\n"); 226 | else 227 | printf("Type is integer\n"); 228 | } else if (type == CFBooleanGetTypeID()) { 229 | printf("Type is boolean\n"); 230 | } else if (type == CFDateGetTypeID()) { 231 | printf("Type is date\n"); 232 | } else if (type == CFArrayGetTypeID()) { 233 | printf("Type is array\n"); 234 | } else if (type == CFDictionaryGetTypeID()) { 235 | printf("Type is dictionary\n"); 236 | } else { 237 | printf("Found a value that is not of a known property list type\n"); 238 | } 239 | CFRelease(result); 240 | return 0; 241 | } 242 | 243 | if ([args[1] isEqualToString:@"export"]) { 244 | if (args.count < 3) { 245 | usage(); 246 | return 255; 247 | } 248 | if (args.count < 4) { 249 | NSLog(@"\nNeed a path to write to"); 250 | return 1; 251 | } 252 | NSArray *keys = (__bridge_transfer NSArray*)_CFPreferencesCopyKeyListWithContainer((__bridge CFStringRef)appid, 253 | kCFPreferencesCurrentUser, host, container); 254 | NSDictionary *out = (__bridge_transfer NSDictionary *)_CFPreferencesCopyMultipleWithContainer( 255 | (__bridge CFArrayRef)keys, (__bridge CFStringRef)appid, 256 | kCFPreferencesCurrentUser, host, container); 257 | if (out == 0) { 258 | NSLog(@"\nThe domain %@ does not exist\n", appid); 259 | return 1; 260 | } 261 | NSError *error; 262 | NSPropertyListFormat format = NSPropertyListBinaryFormat_v1_0; 263 | if ([args[3] isEqualToString:@"-"]) { 264 | format = NSPropertyListXMLFormat_v1_0; 265 | } 266 | 267 | NSData *outData = [NSPropertyListSerialization dataWithPropertyList:out 268 | format:format 269 | options:0 270 | error:&error]; 271 | 272 | if (error) { 273 | NSLog(@"Could not export domain %@ to %@ due to %@", appid, args[3], error); 274 | return 1; 275 | } 276 | if (format == NSPropertyListXMLFormat_v1_0) { 277 | NSFileHandle *fh = [NSFileHandle fileHandleWithStandardOutput]; 278 | [fh writeData:outData]; 279 | } else 280 | [outData writeToFile:args[3] atomically:true]; 281 | return 0; 282 | } 283 | 284 | if ([args[1] isEqualToString:@"import"]) { 285 | if (args.count < 3) { 286 | usage(); 287 | return 255; 288 | } 289 | if (args.count < 4) { 290 | NSLog(@"\nNeed a path to read from"); 291 | return 1; 292 | } 293 | 294 | NSData *inputData; 295 | if ([args[3] isEqualToString:@"-"]) { 296 | NSFileHandle *fh = [NSFileHandle fileHandleWithStandardInput]; 297 | inputData = [fh readDataToEndOfFile]; 298 | } else { 299 | inputData = [NSData dataWithContentsOfFile:args[3]]; 300 | } 301 | if (inputData == nil) { 302 | NSLog(@"Could not read data from %@", args[3]); 303 | return 1; 304 | } 305 | 306 | NSError *error; 307 | NSObject *inputDict = [NSPropertyListSerialization propertyListWithData:inputData 308 | options:0 309 | format:0 310 | error:&error]; 311 | if (error) { 312 | NSLog(@"Could not parse property list from %@ due to %@", args[3], error); 313 | return 1; 314 | } 315 | 316 | if (![inputDict isKindOfClass:[NSDictionary class]]) { 317 | NSLog(@"Property list %@ was not a dictionary\nDefaults have not been changed.\n", inputDict); 318 | return 1; 319 | } 320 | for (NSString *key in [(NSDictionary*)inputDict allKeys]) { 321 | _CFPreferencesSetValueWithContainer((__bridge CFStringRef)key, 322 | (__bridge CFPropertyListRef)[(NSDictionary*)inputDict objectForKey:key], 323 | (__bridge CFStringRef)appid, kCFPreferencesCurrentUser, host, container); 324 | } 325 | _CFPreferencesSynchronizeWithContainer((__bridge CFStringRef)appid, kCFPreferencesCurrentUser, 326 | host, container); 327 | _CFPrefsSynchronizeForProcessTermination(); 328 | return 0; 329 | } 330 | 331 | if ((args.count == 4 || args.count == 3) && ([args[1] isEqualToString:@"delete"] || 332 | /* remove is an undocumented alias for delete */ [args[1] isEqualToString:@"remove"])) { 333 | if (args.count == 4) { 334 | CFPropertyListRef result = _CFPreferencesCopyValueWithContainer((__bridge CFStringRef)args[3], 335 | (__bridge CFStringRef)appid, kCFPreferencesCurrentUser, host, container); 336 | if (result == NULL) { 337 | NSLog(@"\nDomain (%@) not found.\nDefaults have not been changed.\n", appid); 338 | CFRelease(result); 339 | return 1; 340 | } 341 | _CFPreferencesSetValueWithContainer((__bridge CFStringRef)args[3], NULL, (__bridge CFStringRef)appid, 342 | kCFPreferencesCurrentUser, host, container); 343 | Boolean ret = _CFPreferencesSynchronizeWithContainer((__bridge CFStringRef)appid, 344 | kCFPreferencesCurrentUser, host, container); 345 | _CFPrefsSynchronizeForProcessTermination(); 346 | return ret ? 0 : 1; 347 | } else if (args.count == 3) { 348 | CFArrayRef keys = _CFPreferencesCopyKeyListWithContainer((__bridge CFStringRef)appid, 349 | kCFPreferencesCurrentUser, host, container); 350 | if (keys == NULL) { 351 | NSLog(@"\nDomain (%@) not found.\nDefaults have not been changed.\n", appid); 352 | return 1; 353 | } 354 | for (NSString *key in (__bridge NSArray*)keys) { 355 | _CFPreferencesSetValueWithContainer((__bridge CFStringRef)key, NULL, 356 | (__bridge CFStringRef)appid, kCFPreferencesCurrentUser, host, container); 357 | } 358 | Boolean ret = _CFPreferencesSynchronizeWithContainer((__bridge CFStringRef)appid, 359 | kCFPreferencesCurrentUser, host, container); 360 | _CFPrefsSynchronizeForProcessTermination(); 361 | return ret ? 0 : 1; 362 | } 363 | return 1; 364 | } 365 | 366 | if ([args[1] isEqualToString:@"write"]) { 367 | if (args.count < 4) { 368 | usage(); 369 | return 255; 370 | } else { 371 | return defaultsWrite(args, appid, host, container); 372 | } 373 | } 374 | 375 | usage(); 376 | return 255; 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /Sources/helpers.m: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | * 4 | * Copyright (c) 2021 ProcursusTeam 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #import 26 | #import 27 | 28 | #import "NSData+HexString.h" 29 | #include "defaults.h" 30 | 31 | NSString *prettyName(NSString *name) { 32 | if ([name isEqualToString:(__bridge NSString *)kCFPreferencesAnyApplication]) 33 | return @"Apple Global Domain"; 34 | else 35 | return name; 36 | } 37 | 38 | CFPropertyListRef parseTypedArg(NSString *type, NSString *value, bool inArray) { 39 | if (type == NULL) { 40 | if (inArray && ([value characterAtIndex:0] == '{' || [value characterAtIndex:0] == '(')) 41 | return (__bridge CFPropertyListRef)parsePropertyList(value); 42 | return (__bridge CFStringRef)value; 43 | } else if ([type isEqualToString:@"-string"]) { 44 | return (__bridge CFStringRef)value; 45 | } else if ([type isEqualToString:@"-int"] || [type isEqualToString:@"-integer"]) { 46 | return (__bridge CFNumberRef)@([value longLongValue]); 47 | } else if ([type isEqualToString:@"-float"]) { 48 | return (__bridge CFNumberRef)@([value floatValue]); 49 | } else if ([type isEqualToString:@"-bool"] || [type isEqualToString:@"-boolean"]) { 50 | if ([value caseInsensitiveCompare:@"yes"] == NSOrderedSame) 51 | return kCFBooleanTrue; 52 | else if ([value caseInsensitiveCompare:@"true"] == NSOrderedSame) 53 | return kCFBooleanTrue; 54 | else if ([value caseInsensitiveCompare:@"no"] == NSOrderedSame) 55 | return kCFBooleanFalse; 56 | else if ([value caseInsensitiveCompare:@"false"] == NSOrderedSame) 57 | return kCFBooleanFalse; 58 | else { 59 | usage(); 60 | exit(1); 61 | } 62 | } else if ([type isEqualToString:@"-date"]) { 63 | CFDateRef date = (__bridge CFDateRef)[NSDate dateWithString:value]; 64 | if (date == NULL) 65 | date = (__bridge CFDateRef)[NSDate dateWithNaturalLanguageString:value]; 66 | if (date == NULL) { 67 | usage(); 68 | exit(1); 69 | } 70 | return date; 71 | } else if ([type isEqualToString:@"-data"]) { 72 | NSCharacterSet *set = [[NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"] invertedSet]; 73 | if ([value length] % 2 != 0 || [value rangeOfCharacterFromSet:set].location != NSNotFound) { 74 | usage(); 75 | exit(255); 76 | } 77 | return (__bridge CFDataRef)[NSData dataWithHexString:value]; 78 | } 79 | return NULL; 80 | } 81 | 82 | NSObject* parsePropertyList(NSString* propertyList) { 83 | NSObject *rep; 84 | @try { 85 | rep = [propertyList propertyList]; 86 | } 87 | @catch (NSException *e) { 88 | NSLog(@"Could not parse: %@. Try single-quoting it.", propertyList); 89 | exit(1); 90 | } 91 | return rep; 92 | } 93 | 94 | bool isType(NSString* type) { 95 | return [type isEqualToString:@"-string"] || [type isEqualToString:@"-data"] || 96 | [type isEqualToString:@"-int"] || [type isEqualToString:@"-integer"] || 97 | [type isEqualToString:@"-float"] || [type isEqualToString:@"-bool"] || 98 | [type isEqualToString:@"-boolean"] || [type isEqualToString:@"-date"]; 99 | } 100 | 101 | NSArray *flatten(NSObject *input) { 102 | NSMutableArray *ret = [[NSMutableArray alloc] init]; 103 | if ([input isKindOfClass:[NSArray class]]) { 104 | for (NSObject *object in (NSArray*)input) { 105 | if ([object isKindOfClass:[NSString class]]) { 106 | [ret addObject:object]; 107 | } else if ([object isKindOfClass:[NSArray class]]) { 108 | [ret addObjectsFromArray:flatten(object)]; 109 | } else if ([object isKindOfClass:[NSDictionary class]]) { 110 | [ret addObjectsFromArray:flatten(object)]; 111 | } 112 | } 113 | } else { 114 | for (NSString *key in [(NSDictionary*)input allKeys]) { 115 | [ret addObject:key]; 116 | if ([[(NSDictionary*)input objectForKey:key] isKindOfClass:[NSString class]]) { 117 | [ret addObject:[(NSDictionary*)input objectForKey:key]]; 118 | } else if ([[(NSDictionary*)input objectForKey:key] isKindOfClass:[NSArray class]]) { 119 | [ret addObjectsFromArray:flatten([(NSDictionary*)input objectForKey:key])]; 120 | } else if ([[(NSDictionary*)input objectForKey:key] isKindOfClass:[NSDictionary class]]) { 121 | [ret addObjectsFromArray:flatten([(NSDictionary*)input objectForKey:key])]; 122 | } 123 | } 124 | } 125 | return ret; 126 | } 127 | 128 | void usage() 129 | { 130 | printf("Command line interface to a user's defaults.\n"); 131 | printf("Syntax:\n"); 132 | printf("\n"); 133 | printf("'defaults' [-currentHost | -host ] followed by one of the following:\n"); 134 | printf("\n"); 135 | printf(" read shows all defaults\n"); 136 | printf(" read shows defaults for given domain\n"); 137 | printf(" read shows defaults for given domain, key\n"); 138 | printf("\n"); 139 | printf(" read-type shows the type for the given domain, key\n"); 140 | printf("\n"); 141 | printf(" write writes domain (overwrites existing)\n"); 142 | printf(" write writes key for domain\n"); 143 | printf("\n"); 144 | printf(" rename renames old_key to new_key\n"); 145 | printf("\n"); 146 | printf(" delete deletes domain\n"); 147 | printf(" delete deletes key in domain\n"); 148 | printf("\n"); 149 | printf(" import writes the plist at path to domain\n"); 150 | printf(" import - writes a plist from stdin to domain\n"); 151 | printf(" export saves domain as a binary plist to path\n"); 152 | printf(" export - writes domain as an xml plist to stdout\n"); 153 | printf(" domains lists all domains\n"); 154 | printf(" find lists all entries containing word\n"); 155 | printf(" help print this help\n"); 156 | printf("\n"); 157 | printf(" is ( | -app | -globalDomain )\n"); 158 | printf(" or a path to a file omitting the '.plist' extension\n"); 159 | printf("\n [-container ( | | )]\n"); 160 | printf(" may be specified before the domain name to change the container\n"); 161 | printf(" this is a Procursus extension\n"); 162 | printf("\n"); 163 | printf(" is one of:\n"); 164 | printf(" \n"); 165 | printf(" -string \n"); 166 | printf(" -data \n"); 167 | printf(" -int[eger] \n"); 168 | printf(" -float \n"); 169 | printf(" -bool[ean] (true | false | yes | no)\n"); 170 | printf(" -date \n"); 171 | printf(" -array ...\n"); 172 | printf(" -array-add ...\n"); 173 | printf(" -dict ...\n"); 174 | printf(" -dict-add ...\n"); 175 | printf("\nContact the Procursus Team for support.\n"); 176 | } 177 | -------------------------------------------------------------------------------- /Sources/write.m: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | * 4 | * Copyright (c) 2020-present quiprr 5 | * Modified work Copyright (c) 2021 ProcursusTeam 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #import 27 | #import 28 | 29 | #import "NSData+HexString.h" 30 | #include "defaults.h" 31 | 32 | /* 33 | * write writes domain (overwrites existing) 34 | * write writes key for domain 35 | * 36 | * is one of: 37 | * 38 | * -string 39 | * -data 40 | * -int[eger] 41 | * -float 42 | * -bool[ean] (true | false | yes | no) 43 | * -date 44 | * -array ... 45 | * -array-add ... 46 | * -dict ... 47 | * -dict-add ... 48 | */ 49 | 50 | int defaultsWrite(NSArray *args, NSString *ident, CFStringRef host, CFStringRef container) { 51 | if (args.count == 4) { 52 | NSObject* rep = parsePropertyList(args[3]); 53 | 54 | if (![rep isKindOfClass:[NSDictionary class]]) { 55 | NSLog(@"\nRep argument is not a dictionary\nDefaults have not been changed.\n"); 56 | return 1; 57 | } 58 | 59 | NSMutableArray *toRemove; 60 | 61 | NSArray *keys = (__bridge_transfer NSArray*)_CFPreferencesCopyKeyListWithContainer( 62 | (__bridge CFStringRef)ident, 63 | kCFPreferencesCurrentUser, host, container); 64 | if (keys.count == 0) { 65 | toRemove = NULL; 66 | } else { 67 | toRemove = [keys mutableCopy]; 68 | [toRemove removeObjectsInArray:[(NSDictionary *)rep allKeys]]; 69 | } 70 | 71 | for (NSString *key in [(NSDictionary *)rep allKeys]) { 72 | _CFPreferencesSetValueWithContainer((__bridge CFStringRef)key, 73 | (__bridge CFPropertyListRef)[(NSDictionary*)rep objectForKey:key], 74 | (__bridge CFStringRef)ident, kCFPreferencesCurrentUser, host, container); 75 | } 76 | for (NSString *key in toRemove) { 77 | _CFPreferencesSetValueWithContainer((__bridge CFStringRef)key, NULL, 78 | (__bridge CFStringRef)ident, kCFPreferencesCurrentUser, host, container); 79 | } 80 | _CFPreferencesSynchronizeWithContainer((__bridge CFStringRef)ident, kCFPreferencesCurrentUser, 81 | host, container); 82 | _CFPrefsSynchronizeForProcessTermination(); 83 | return 0; 84 | } 85 | 86 | if (args.count == 5) { 87 | NSObject *rep; 88 | // Should probably clean this up 89 | if (isType(args[4])) { 90 | usage(); 91 | return 255; 92 | } else if ([args[4] isEqualToString:@"-array"]) { 93 | rep = [[NSArray alloc] init]; 94 | } else if ([args[4] isEqualToString:@"-dict"]) { 95 | rep = [[NSDictionary alloc] init]; 96 | } else if ([args[4] isEqualToString:@"-array-add"] || [args[4] isEqualToString:@"-dict-add"]) { 97 | rep = nil; 98 | } else { 99 | rep = parsePropertyList(args[4]); 100 | } 101 | if (rep != nil) { 102 | _CFPreferencesSetValueWithContainer((__bridge CFStringRef)args[3], (__bridge CFPropertyListRef)rep, 103 | (__bridge CFStringRef)ident, kCFPreferencesCurrentUser, host, container); 104 | Boolean ret = _CFPreferencesSynchronizeWithContainer((__bridge CFStringRef)ident, 105 | kCFPreferencesCurrentUser, host, container); 106 | _CFPrefsSynchronizeForProcessTermination(); 107 | if (!ret) { 108 | NSLog(@"Could not write domain %@; exiting", prettyName(ident)); 109 | return 1; 110 | } 111 | } 112 | return 0; 113 | } else if (args.count >= 6) { 114 | CFPropertyListRef value = NULL; 115 | NSMutableArray *array = [[NSMutableArray alloc] init]; 116 | NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; 117 | if ([args[4] isEqualToString: @"-array"] || [args[4] isEqualToString: @"-array-add"]) { // Array 118 | if ([args[4] isEqualToString: @"-array-add"]) { 119 | CFPropertyListRef prepend = _CFPreferencesCopyValueWithContainer((__bridge CFStringRef)args[3], 120 | (__bridge CFStringRef)ident, kCFPreferencesCurrentUser, host, container); 121 | if (prepend != NULL) { 122 | if (CFGetTypeID(prepend) != CFArrayGetTypeID()) { 123 | NSLog(@"Value for key %@ is not an array; cannot append. Leaving defaults unchanged.\n", args[3]); 124 | return 1; 125 | } 126 | [array addObjectsFromArray:(__bridge_transfer NSArray*)prepend]; 127 | } 128 | } 129 | NSArray *arrayItems = [args subarrayWithRange:NSMakeRange(5, args.count - 5)]; 130 | for (int i = 0; i < arrayItems.count; i++) { 131 | if ([arrayItems[i] isEqualToString:@"-array"] || [arrayItems[i] isEqualToString:@"-dict"]) { 132 | NSLog(@"Cannot nest composite types (arrays and dictionaries); exiting"); 133 | return 1; 134 | } 135 | if (isType(arrayItems[i])) { 136 | if (i >= arrayItems.count - 1) { 137 | usage(); 138 | return 255; 139 | } 140 | [array addObject: (__bridge NSObject*)parseTypedArg(arrayItems[i], arrayItems[++i], true)]; 141 | } else { 142 | [array addObject: (__bridge NSObject*)parseTypedArg(NULL, arrayItems[i], true)]; 143 | } 144 | } 145 | value = (__bridge CFPropertyListRef)array; 146 | } else if ([args[4] isEqualToString: @"-dict"] || [args[4] isEqualToString: @"-dict-add"]) { // Dictionary 147 | if ([args[4] isEqualToString: @"-dict-add"]) { 148 | CFPropertyListRef prepend = _CFPreferencesCopyValueWithContainer((__bridge CFStringRef)args[3], 149 | (__bridge CFStringRef)ident, kCFPreferencesCurrentUser, host, container); 150 | if (prepend != NULL) { 151 | if (CFGetTypeID(prepend) != CFDictionaryGetTypeID()) { 152 | NSLog(@"Value for key %@ is not a dictionary; cannot append. Leaving defaults unchanged.\n", args[3]); 153 | return 1; 154 | } 155 | [dict addEntriesFromDictionary:(__bridge_transfer NSDictionary*)prepend]; 156 | } 157 | } 158 | NSArray *arrayItems = [args subarrayWithRange:NSMakeRange(5, args.count - 5)]; 159 | for (int i = 0; i < arrayItems.count; i++) { 160 | if ([arrayItems[i] isEqualToString:@"-array"] || [arrayItems[i] isEqualToString:@"-dict"]) { 161 | NSLog(@"Cannot nest composite types (arrays and dictionaries); exiting\n"); 162 | return 1; 163 | } 164 | if (isType(arrayItems[i])) { 165 | if ([arrayItems[i] isEqualToString:@"-string"]) { 166 | i++; 167 | } else { 168 | if (arrayItems.count == 1) { 169 | usage(); 170 | return 255; 171 | } else 172 | NSLog(@"Dictionary keys must be strings\n"); 173 | return 1; 174 | } 175 | } 176 | if (i == arrayItems.count - 1) { 177 | NSLog(@"Key %@ lacks a corresponding value\n", arrayItems[i]); 178 | return 1; 179 | } 180 | if (isType(arrayItems[i + 1])) { 181 | if (i >= arrayItems.count - 2) { 182 | usage(); 183 | return 255; 184 | } 185 | [dict setObject:(__bridge_transfer NSObject*)parseTypedArg(arrayItems[i + 1], arrayItems[i + 2], true) 186 | forKey:arrayItems[i]]; 187 | i += 2; 188 | } else { 189 | [dict setObject:(__bridge_transfer NSObject*)parseTypedArg(NULL, arrayItems[i + 1], true) 190 | forKey:arrayItems[i]]; 191 | i++; 192 | } 193 | } 194 | value = (__bridge CFPropertyListRef)dict; 195 | } else { 196 | value = parseTypedArg(args[4], args[5], false); 197 | 198 | if (isType(args[4]) && args.count >= 7) { 199 | NSLog(@"Unexpected argument %@; leaving defaults unchanged.\n", args[6]); 200 | return 1; 201 | } else if (!isType(args[4]) && args.count >= 6) { 202 | NSLog(@"Unexpected argument %@; leaving defaults unchanged.\n", args[5]); 203 | return 1; 204 | } 205 | } 206 | 207 | _CFPreferencesSetValueWithContainer((__bridge CFStringRef)args[3], value, 208 | (__bridge CFStringRef)ident, kCFPreferencesCurrentUser, host, container); 209 | _CFPreferencesSynchronizeWithContainer((__bridge CFStringRef)ident, kCFPreferencesCurrentUser, host, container); 210 | _CFPrefsSynchronizeForProcessTermination(); 211 | return 0; 212 | } 213 | 214 | return 1; 215 | } 216 | -------------------------------------------------------------------------------- /ent.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | platform-application 5 | 6 | com.apple.private.security.no-container 7 | 8 | com.apple.private.skip-library-validation 9 | 10 | com.apple.private.trust-defaults-kvstore-identifier 11 | 12 | com.apple.private.MobileContainerManager.allowed 13 | 14 | 15 | 16 | --------------------------------------------------------------------------------