CloverBootloader/CloverPackage/CloverPrefpane/src/CloverPrefpane.m
2019-09-03 12:58:42 +03:00

738 lines
26 KiB
Objective-C

//
// CloverPrefpane.m
// CloverPrefpane
//
// Created by JrCs on 03/05/13.
// Copyright (c) 2013 ProjectOSX. All rights reserved.
//
#import "CloverPrefpane.h"
#include <mach/mach_error.h>
#define kCloverInstaller @"com.projectosx.clover.installer"
// Global Variables
static const CFStringRef agentIdentifier=CFSTR("com.projectosx.Clover.Updater");
static const CFStringRef agentExecutable=CFSTR("/Library/Application Support/Clover/CloverUpdaterUtility");
static const CFStringRef checkIntervalKey=CFSTR("ScheduledCheckInterval");
static const CFStringRef lastCheckTimestampKey=CFSTR("LastCheckTimestamp");
static const CFStringRef efiDirPathKey=CFSTR("EFI Directory Path");
static NSString* kLogLineCount = @"LogLineCount";
static NSString* kLogEveryBoot = @"LogEveryBoot";
static NSString* kBackupDirOnDestVol = @"BackupDirOnDestVol";
static NSString* kKeepBackupLimit = @"KeepBackupLimit";
static NSString* kMountEFI = @"MountEFI";
static NSString* kNVRamDisk = @"NVRamDisk";
@implementation CloverPrefpane
@synthesize cloverLogEveryBootEnabled; // = _cloverLogEveryBootEnabled;
@synthesize cloverLogEveryBootLimit; // = _cloverLogEveryBootLimit;
@synthesize diskutilList; // = _diskutilList;
@synthesize efiPartitions; // = _efiPartitions;
@synthesize nvRamPartitions; // = _nvRamPartitions;
@synthesize nvram; // = _nvram;
@synthesize themeInfo; // = _themeInfo;
#pragma mark Properties
- (NSDictionary *)diskutilList {
if (diskutilList == nil) {
// Get diskutil list -plist output
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath: @"/usr/sbin/diskutil"];
[task setArguments:[NSArray arrayWithObjects: @"list", @"-plist", nil]];
NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file = [pipe fileHandleForReading];
[task launch];
NSData *data = [file readDataToEndOfFile];
diskutilList = CFBridgingRelease(CFPropertyListCreateWithData(kCFAllocatorDefault,
(__bridge CFDataRef)(data),
kCFPropertyListImmutable,
NULL, NULL));
}
return diskutilList;
}
// Hack to avoid warning generated by genstrings
#define GetLocalized\
String(key, comment) NSLocalized\
StringFromTableInBundle(key, nil, self.bundle, comment)
#define AddKeyValueToList(list, title, value) \
[list addObject:[NSDictionary dictionaryWithObjectsAndKeys: \
(title), @"Title", \
(value ? value : [NSNull null]), @"Value", nil]]
- (NSArray*)efiPartitions {
if (efiPartitions == nil) {
NSMutableArray *list = [[NSMutableArray alloc] init];
AddKeyValueToList(list,
GetLocalizedString(@"No",@"Not mounting EFI partition"),
nil);
AddKeyValueToList(list,
GetLocalizedString(@"Boot Volume",@"EFI partition from boot volume"),
[NSNumber numberWithBool:YES]);
NSArray *disksAndPartitions = [[self diskutilList] objectForKey:@"AllDisksAndPartitions"];
if (disksAndPartitions != nil) {
for (NSDictionary *diskEntry in disksAndPartitions) {
NSString *content = [diskEntry objectForKey:@"Content"];
if (content != nil) {
// Disk has partitions
if ([content isEqualToString:@"GUID_partition_scheme"] ||
[content isEqualToString:@"FDisk_partition_scheme"]) {
NSString *diskIdentifier = [diskEntry objectForKey:@"DeviceIdentifier"];
NSArray *partitions = [diskEntry objectForKey:@"Partitions"];
if (diskIdentifier != nil && partitions != nil) {
NSMutableArray *volumeNames = [[NSMutableArray alloc] init];
NSString *espIdentifier = nil;
for (NSDictionary *partitionEntry in partitions) {
NSString *content = [partitionEntry objectForKey:@"Content"];
if (content != nil && [content isEqualToString:@"EFI"]) {
NSString *identifier = [partitionEntry objectForKey:@"DeviceIdentifier"];
if (identifier != nil) {
espIdentifier = [[self getPartitionProperties:identifier] objectForKey:@"UUID"];
if (!espIdentifier) {
espIdentifier = identifier;
}
}
}
NSString *volumeName = [partitionEntry objectForKey:@"VolumeName"];
if (volumeName) {
[volumeNames addObject:volumeName];
}
}
if (espIdentifier) {
NSString *name = [NSString stringWithFormat:GetLocalizedString(@"ESP on [%@] %@", nil),
diskIdentifier, [volumeNames componentsJoinedByString:@", "]];
AddKeyValueToList(list, name, espIdentifier);
}
}
}
}
}
}
efiPartitions = [NSArray arrayWithArray:list];
}
return efiPartitions;
}
- (NSArray*)nvRamPartitions {
if (nil == nvRamPartitions) {
NSMutableArray *list = [[NSMutableArray alloc] init];
AddKeyValueToList(list, GetLocalizedString(@"Never",nil), [NSNumber numberWithBool:NO]);
AddKeyValueToList(list, GetLocalizedString(@"Auto",nil), [NSNull null]);
AddKeyValueToList(list, GetLocalizedString(@"Always",nil), [NSNumber numberWithBool:YES]);
nvRamPartitions = [NSArray arrayWithArray:list];
}
return nvRamPartitions;
}
- (IBAction)updateCloverLogEveryBoot:(id)sender {
NSString* logEveryBoot;
if ([self.cloverLogEveryBootEnabled boolValue] == YES) {
unsigned int logEveryBootLimit = [self.cloverLogEveryBootLimit unsignedIntValue];
if (logEveryBootLimit > 0) {
logEveryBoot = [NSString stringWithFormat:@"%d", logEveryBootLimit];
} else {
logEveryBoot=@"Yes";
}
}
else
logEveryBoot=@"";
[self.nvram setValue:logEveryBoot forKey:kLogEveryBoot];
[self updateCloverNVRamVariables:nil];
}
- (IBAction)updateCloverNVRamVariables:(id)sender {
Class boolClass = [[NSNumber numberWithBool:YES] class];
Class nullClass = [NSNull class];
for (id key in [self.nvram allKeys]) {
id value = [self.nvram valueForKey:key];
if ([value isKindOfClass:boolClass]) {
value = [value boolValue] ? @"Yes" : @"No";
} else if ([value isKindOfClass:nullClass]) {
value = @"";
} else {
value = [NSString stringWithFormat:@"%@",value];
}
[self setNVRamKey:[NSString stringWithFormat:@"Clover.%@", key] Value:value];
}
}
#pragma mark Methods
- (NSDictionary*)getPartitionProperties:(NSString*)bsdName {
CFMutableDictionaryRef matchingDict;
io_service_t service;
NSDictionary *result = nil;
matchingDict = IOBSDNameMatching(kIOMasterPortDefault, 0, [bsdName UTF8String]);
if (matchingDict == NULL) {
NSLog(@"IOBSDNameMatching returned a NULL dictionary");
} else {
// Fetch the object with the matching BSD node name.
// Note that there should only be one match, so IOServiceGetMatchingService is used instead of
// IOServiceGetMatchingServices to simplify the code.
service = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
if (IO_OBJECT_NULL == service) {
NSLog(@"IOServiceGetMatchingService returned IO_OBJECT_NULL");
} else {
if (IOObjectConformsTo(service, "IOMedia")) {
CFMutableDictionaryRef properties;
IORegistryEntryCreateCFProperties(service,&properties, kCFAllocatorDefault, 0);
result = (__bridge_transfer id)properties;
}
IOObjectRelease(service);
}
}
return result;
}
#pragma mark Events
// System Preferences calls this method when the pane is initialized.
- (id)initWithBundle:(NSBundle *)bundle {
if ( ( self = [super initWithBundle:bundle] ) != nil ) {
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *agentsFolder = [[searchPaths objectAtIndex:0] stringByAppendingPathComponent:@"LaunchAgents"];
[[NSFileManager defaultManager]
createDirectoryAtPath:agentsFolder
withIntermediateDirectories:YES
attributes:nil
error:nil];
agentPlistPath = [[NSString alloc]
initWithString:[[agentsFolder stringByAppendingPathComponent:(NSString *)CFBridgingRelease(agentIdentifier)]
stringByAppendingPathExtension:@"plist"]];
// Allocate object for accessing IORegistry
mach_port_t masterPort;
kern_return_t result = IOMasterPort(bootstrap_port, &masterPort);
if (result != KERN_SUCCESS) {
NSLog(@"Error getting the IOMaster port: %s", mach_error_string(result));
exit(1);
}
_ioRegEntryRef = IORegistryEntryFromPath(masterPort, "IODeviceTree:/options");
if (_ioRegEntryRef == 0) {
NSLog(@"nvram is not supported on this system");
}
diskutilList = nil;
// Init NVRam variables fields
[self initNVRamVariableFields];
}
return self;
}
- (void)mainViewDidLoad {
BOOL plistExists;
// Initialize revision fields
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSLocalDomainMask, YES);
NSString *preferenceFolder = [[searchPaths objectAtIndex:0] stringByAppendingPathComponent:@"Preferences"];
NSString *cloverInstallerPlist = [[preferenceFolder stringByAppendingPathComponent:kCloverInstaller] stringByAppendingPathExtension:@"plist"];
plistExists = [[NSFileManager defaultManager] fileExistsAtPath:cloverInstallerPlist];
NSString* installedRevision = @"-";
if (plistExists) {
NSDictionary *dict = [[NSDictionary alloc]
initWithContentsOfFile:cloverInstallerPlist];
NSNumber* revision = [dict objectForKey:@"CloverRevision"];
if (revision)
installedRevision = [revision stringValue];
}
[lastInstalledRevision setStringValue:installedRevision];
NSString* bootedRevision = @"-";
io_registry_entry_t ioRegistryEFI = IORegistryEntryFromPath(kIOMasterPortDefault,
"IODeviceTree:/efi/platform");
if (ioRegistryEFI) {
CFStringRef nameRef = CFStringCreateWithCString(kCFAllocatorDefault,
"clovergui-revision",
kCFStringEncodingUTF8);
if (nameRef) {
CFTypeRef valueRef = IORegistryEntryCreateCFProperty(ioRegistryEFI, nameRef, 0, 0);
CFRelease(nameRef);
if (valueRef) {
// Get the OF variable's type.
CFTypeID typeID = CFGetTypeID(valueRef);
if (typeID == CFDataGetTypeID())
bootedRevision = [NSString stringWithFormat:@"%u",*((uint32_t*)CFDataGetBytePtr(valueRef))];
CFRelease(valueRef);
}
}
IOObjectRelease(ioRegistryEFI);
}
[lastBootedRevision setStringValue:bootedRevision];
// Initialize popUpCheckInterval
unsigned int checkInterval =
[self getUIntPreferenceKey:checkIntervalKey forAppID:agentIdentifier withDefault:0];
[popUpCheckInterval selectItemWithTag:checkInterval];
// Initialize LastRunDate
unsigned int lastCheckTimestamp =
[self getUIntPreferenceKey:lastCheckTimestampKey forAppID:agentIdentifier withDefault:0];
if (lastCheckTimestamp == 0) {
[LastRunDate setStringValue:@"-"];
} else {
NSDate *date = [NSDate dateWithTimeIntervalSince1970:lastCheckTimestamp];
[LastRunDate setStringValue:[LastRunDate.formatter stringFromDate:date]];
}
// Enable the checkNowButton if executable is present
[checkNowButton setEnabled:
[[NSFileManager defaultManager] fileExistsAtPath:(NSString*)CFBridgingRelease(agentExecutable)]];
// Get EFI Path
NSString* efiDir=[self getStringPreferenceKey:efiDirPathKey
forAppID:(CFStringRef)[self.bundle bundleIdentifier]
withDefault:CFSTR("/")];
[_EFIPathControl setURL:[NSURL fileURLWithPath:efiDir]];
[self initThemeTab:efiDir];
// Setup security.
AuthorizationItem items = {kAuthorizationRightExecute, 0, NULL, 0};
AuthorizationRights rights = {1, &items};
[authView setAuthorizationRights:&rights];
authView.delegate = self;
[authView updateStatus:nil];
plistExists = [[NSFileManager defaultManager] fileExistsAtPath:agentPlistPath];
if (plistExists && checkInterval == 0) {
[[NSFileManager defaultManager] removeItemAtPath:agentPlistPath error:nil];
}
}
- (void) initNVRamVariableFields {
NSString *value;
self.nvram = [NSMutableDictionary dictionaryWithCapacity:16]; // 16 entries for the moment
value = [self getCloverNVRam:kLogLineCount];
[self.nvram setValue:value forKey:kLogLineCount];
value = [self getCloverNVRam:kLogEveryBoot];
[self.nvram setValue:value
forKey:kLogEveryBoot];
if ([value length] == 0) {
self.cloverLogEveryBootEnabled = [NSNumber numberWithBool:NO];
self.cloverLogEveryBootLimit = [NSNumber numberWithInt:0];
} else {
self.cloverLogEveryBootEnabled = [value boolValue] ?
[NSNumber numberWithBool:YES] :
[NSNumber numberWithBool:NO];
self.cloverLogEveryBootLimit = [NSNumber numberWithInteger:[value integerValue]];
}
value = [self getCloverNVRam:kBackupDirOnDestVol];
[self.nvram setValue:value
forKey:kBackupDirOnDestVol];
value = [self getCloverNVRam:kKeepBackupLimit];
if ([value length] == 0)
value = @"0";
[self.nvram setValue:value
forKey:kKeepBackupLimit];
value = [self getCloverNVRam:kMountEFI];
[self.nvram setValue:value forKey:kMountEFI];
value = [self getCloverNVRam:kNVRamDisk];
[self.nvram setValue:value forKey:kNVRamDisk];
}
#pragma mark -
#pragma mark General Tab Methods
- (IBAction) configureAutomaticUpdates:(id)sender {
CFDictionaryRef launchInfo = SMJobCopyDictionary(kSMDomainUserLaunchd, agentIdentifier);
if (launchInfo != NULL) {
CFRelease(launchInfo);
CFErrorRef error = NULL;
if (!SMJobRemove(kSMDomainUserLaunchd, agentIdentifier, NULL, YES, &error))
NSLog(@"Error in SMJobRemove: %@", error);
if (error)
CFRelease(error);
}
NSInteger checkInterval = [sender tag];
[self setPreferenceKey:checkIntervalKey forAppID:agentIdentifier fromInt:(int)checkInterval];
if (checkInterval > 0 && [[NSFileManager defaultManager] fileExistsAtPath:(NSString*)CFBridgingRelease(agentExecutable)]) {
// Create a new plist
NSArray* call = [NSArray arrayWithObjects:
(NSString *)CFBridgingRelease(agentExecutable),
@"startup",
nil];
NSDictionary *plist = [NSDictionary dictionaryWithObjectsAndKeys:
(NSString *)CFBridgingRelease(agentIdentifier), @"Label",
[NSNumber numberWithInteger:checkInterval], @"StartInterval",
[NSNumber numberWithBool:YES], @"RunAtLoad",
(NSString *)CFBridgingRelease(agentExecutable), @"Program",
call, @"ProgramArguments",
nil];
[plist writeToFile:agentPlistPath atomically:YES];
CFErrorRef error = NULL;
if (!SMJobSubmit(kSMDomainUserLaunchd, (__bridge CFDictionaryRef)(plist), NULL, &error)) {
if (error) {
NSLog(@"Error in SMJobSubmit: %@", error);
} else
NSLog(@"Error in SMJobSubmit without details. Check /var/db/launchd.db/com.apple.launchd.peruser.NNN/overrides.plist for %@ set to disabled.", agentIdentifier);
}
if (error)
CFRelease(error);
} else {
// Remove the plist
[[NSFileManager defaultManager] removeItemAtPath:agentPlistPath error:nil];
}
CFPreferencesAppSynchronize(agentIdentifier); // Force the preferences to be save to disk
}
- (IBAction)checkNow:(id)sender {
[[NSWorkspace sharedWorkspace] launchApplication:(NSString*)CFBridgingRelease(agentExecutable)];
}
- (BOOL)isUnlocked {
return [authView authorizationState] == SFAuthorizationViewUnlockedState;
}
#pragma mark -
#pragma mark Theme Tab Methods
- (void) initThemeTab:(NSString*) efiDir {
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDir = NO;
[_cloverThemeComboBox removeAllItems]; // Remove all theme names if exists
NSString *themesDir = [efiDir stringByAppendingPathComponent:@"CLOVER/Themes"];
[fileManager fileExistsAtPath:themesDir isDirectory:(&isDir)];
[_themeWarning setHidden:isDir];
// get the list of all files and directories
NSMutableArray *themes = [[NSMutableArray alloc] init];
NSArray *fileList = [fileManager contentsOfDirectoryAtPath:themesDir error:nil];
if (fileList) {
for(NSString *file in fileList) {
NSString *path = [[themesDir stringByAppendingPathComponent:file]
stringByAppendingPathComponent:@"theme.plist"];
if ([fileManager fileExistsAtPath:path]) {
[themes addObject:file];
}
}
}
else {
// Try to get installed themes
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSLocalDomainMask, YES);
NSString *preferenceFolder = [[searchPaths objectAtIndex:0] stringByAppendingPathComponent:@"Preferences"];
NSString *cloverInstallerPlist = [[preferenceFolder stringByAppendingPathComponent:kCloverInstaller] stringByAppendingPathExtension:@"plist"];
NSDictionary *dict = [[NSDictionary alloc]
initWithContentsOfFile:cloverInstallerPlist];
if (dict) {
NSArray *installedThemes = [dict objectForKey:@"InstalledThemes"];
[themes addObjectsFromArray:installedThemes];
}
else {
// Get default themes from bundle
NSString* defaultThemePlistPath = [self.bundle pathForResource:@"DefaultThemes.plist" ofType:@"plist"];
NSDictionary *dict = [[NSDictionary alloc]
initWithContentsOfFile:defaultThemePlistPath];
if (dict) {
NSArray *defaultThemes = [dict objectForKey:@"Default Themes"];
if (defaultThemes)
[themes addObjectsFromArray:defaultThemes];
}
}
}
NSString* currentTheme = [self getNVRamKey:@"Clover.Theme"];
if (currentTheme && [themes indexOfObject:currentTheme] == NSNotFound)
[themes addObject:currentTheme];
NSArray *sortedThemes = [themes sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
[_cloverThemeComboBox addItemsWithObjectValues:sortedThemes];
if (currentTheme) {
[_cloverThemeComboBox selectItemWithObjectValue:currentTheme];
[self updateThemeTab:currentTheme];
}
}
- (void) updateThemeTab:(NSString*) themeName {
NSString *efiDir = [[_EFIPathControl URL] path];
NSString *themeDir = [[efiDir stringByAppendingPathComponent:@"CLOVER/Themes"]
stringByAppendingPathComponent:themeName];
// Load the theme.plist file
NSString *themePlistPath = [themeDir stringByAppendingPathComponent:@"theme.plist"];
NSMutableDictionary *newThemeInfo = [NSMutableDictionary dictionaryWithContentsOfFile:themePlistPath];
if (newThemeInfo) {
self.themeInfo = newThemeInfo;
} else {
self.themeInfo = [NSMutableDictionary dictionaryWithCapacity:16]; // 16 entries for the moment
}
NSString *imagePath = [themeDir stringByAppendingPathComponent:@"screenshot.png"];
NSNumber *previewAvailable = [NSNumber numberWithBool:YES];
if (![[NSFileManager defaultManager] fileExistsAtPath:imagePath]) {
imagePath = [self.bundle pathForResource:@"NoPreview" ofType:@"png"];
previewAvailable = [NSNumber numberWithBool:NO];
}
[self.themeInfo setObject:previewAvailable forKey:@"PreviewAvailable"];
NSImage* preview = [[NSImage alloc] initWithContentsOfFile:imagePath];
[self.themeInfo setObject:preview forKey:@"Preview"];
if (![self.themeInfo objectForKey:@"Author"]) {
[self.themeInfo setObject:GetLocalizedString(@"Unknown",nil) forKey:@"Author"];
}
if (![self.themeInfo objectForKey:@"Year"]) {
[self.themeInfo setObject:GetLocalizedString(@"Unknown",nil) forKey:@"Year"];
}
if (![self.themeInfo objectForKey:@"Description"]) {
[self.themeInfo setObject:GetLocalizedString(@"No description available",nil) forKey:@"Description"];
}
// Update NVRam
NSString *oldValue = [self getNVRamKey:@"Clover.Theme"];
if ((oldValue.length != 0 || themeName.length != 0) &&
![oldValue isEqualToString:themeName]) {
if ([self setNVRamKey:@"Clover.Theme" Value:themeName] != 0) {
[_cloverThemeComboBox setStringValue:oldValue];
[self updateThemeTab:oldValue];
}
}
}
- (IBAction)showPathOpenPanel:(id)sender {
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setAllowsMultipleSelection:NO];
[panel setCanChooseDirectories:YES];
[panel setCanChooseFiles:NO];
[panel setResolvesAliases:YES];
NSString *panelTitle = GetLocalizedString(@"Select the EFI directory", @"Title for the open panel");
[panel setTitle:panelTitle];
NSString *promptString = GetLocalizedString(@"Set EFI directory", @"Prompt for the open panel prompt");
[panel setPrompt:promptString];
[panel beginSheetModalForWindow:[sender window] completionHandler:^(NSInteger result) {
// Hide the open panel.
[panel orderOut:self];
// If the return code wasn't OK, don't do anything.
if (result != NSOKButton) {
return;
}
// Get the first URL returned from the Open Panel and set it at the first path component of the control.
NSURL *url = [[panel URLs] objectAtIndex:0];
[self->_EFIPathControl setURL:url];
NSString *efiDir = [url path];
[self setPreferenceKey:efiDirPathKey
forAppID:(CFStringRef)[self.bundle bundleIdentifier]
fromString:(CFStringRef)efiDir];
CFPreferencesAppSynchronize((CFStringRef)[self.bundle bundleIdentifier]); // Force the preferences to be save to disk
[self initThemeTab:efiDir];
}];
}
- (IBAction)themeComboBox:(NSComboBox*)sender {
NSString *themeName = [sender stringValue];
[self updateThemeTab:themeName];
}
#pragma mark -
#pragma mark Authorization delegates
//
// SFAuthorization delegates
//
- (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view {
}
- (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view {
}
//
// NVRAM methods
//
#pragma mark -
#pragma mark NVRam methods
// Get Clover NVRAM variable
-(NSString*) getCloverNVRam:(const NSString*)key {
return [self getNVRamKey:[NSString stringWithFormat:@"Clover.%@",key]];
}
// Get NVRAM value
-(NSString*) getNVRamKey:(const NSString*)key {
NSString* result = @"";
CFTypeRef valueRef = IORegistryEntryCreateCFProperty(_ioRegEntryRef, (__bridge CFStringRef)(key), 0, 0);
if (valueRef == 0) {
return result;
}
// Get the OF variable's type.
CFTypeID typeID = CFGetTypeID(valueRef);
if (typeID == CFDataGetTypeID()) {
result = [NSString stringWithUTF8String:(const char*)CFDataGetBytePtr(valueRef)];
}
CFRelease(valueRef);
return result;
}
// Set NVRAM key/value pair
- (OSErr) setNVRamKey:(const NSString*)key Value:(NSString*)value {
OSErr processError = 0;
if (key) {
if (!value) {
value=@"";
}
NSString *oldValue = [self getNVRamKey:key];
if ((oldValue.length != 0 || value.length != 0) &&
![oldValue isEqualToString:value]) {
// Size for key=value + null terminal char
size_t len = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1
+ [value lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
char* nvram_arg=(char*) malloc(sizeof(char) * len);
snprintf(nvram_arg, len, "%s=%s", [key UTF8String], [value UTF8String]);
// Need 2 parameters: key=value and NULL
const char **argv = (const char **)malloc(sizeof(char *) * 2);
argv[0] = nvram_arg;
argv[1] = NULL;
processError = AuthorizationExecuteWithPrivileges([[authView authorization] authorizationRef],
[@"/usr/sbin/nvram" UTF8String],
kAuthorizationFlagDefaults,
(char *const *)argv,
nil);
if (processError != errAuthorizationSuccess)
NSLog(@"Error trying to set nvram %s:%d", nvram_arg, processError);
free(argv);
free(nvram_arg);
}
}
return processError;
}
//
// Preferences methods
//
#pragma mark -
#pragma mark Preferences methods
// get and set preference keys functions idea taken from:
// http://svn.perian.org/branches/perian-1.1/CPFPerianPrefPaneController.m
- (unsigned int)getUIntPreferenceKey:(CFStringRef)key
forAppID:(CFStringRef)appID
withDefault:(unsigned int)defaultValue {
CFPropertyListRef value;
unsigned int ret = defaultValue;
value = CFPreferencesCopyAppValue(key, appID);
if (value && CFGetTypeID(value) == CFNumberGetTypeID())
CFNumberGetValue(value, kCFNumberIntType, &ret);
if (value) {
CFRelease(value);
}
return ret;
}
- (void)setPreferenceKey:(CFStringRef)key
forAppID:(CFStringRef)appID
fromInt:(int)value {
CFNumberRef numRef = CFNumberCreate(NULL, kCFNumberIntType, &value);
CFPreferencesSetAppValue(key, numRef, appID);
CFRelease(numRef);
}
- (NSString *)getStringPreferenceKey:(CFStringRef)key
forAppID:(CFStringRef)appID
withDefault:(CFStringRef)defaultValue {
CFPropertyListRef value;
NSString *ret = nil;
value = CFPreferencesCopyAppValue(key, appID);
if(value && CFGetTypeID(value) == CFStringGetTypeID())
ret = [NSString stringWithString:(__bridge NSString * _Nonnull)(value)];
else
ret = [NSString stringWithString:(__bridge NSString * _Nonnull)(defaultValue)];
if(value) {
CFRelease(value);
}
return ret;
}
- (void)setPreferenceKey:(CFStringRef)key
forAppID:(CFStringRef)appID
fromString:(CFStringRef)value {
CFPreferencesSetAppValue(key, value, appID);
}
@end