diff options
Diffstat (limited to 'mDNSResponder/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m')
-rw-r--r-- | mDNSResponder/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m | 1194 |
1 files changed, 1194 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m b/mDNSResponder/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m new file mode 100644 index 00000000..063bc724 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m @@ -0,0 +1,1194 @@ +/* + File: DNSServiceDiscoveryPref.m + + Abstract: System Preference Pane for Dynamic DNS and Wide-Area DNS Service Discovery + + Copyright: (c) Copyright 2005-2011 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under Apple's + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DNSServiceDiscoveryPref.h" +#import "ConfigurationAuthority.h" +#import "PrivilegedOperations.h" +#import <unistd.h> + +#include "../../Clients/ClientCommon.h" + +#ifndef NSINTEGER_DEFINED +#define NSInteger int +#endif + +@implementation DNSServiceDiscoveryPref + +static NSInteger +MyArrayCompareFunction(id val1, id val2, void *context) +{ + (void)context; // Unused + return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive); +} + +static NSInteger +MyDomainArrayCompareFunction(id val1, id val2, void *context) +{ + (void)context; // Unused + NSString *domain1 = [val1 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + NSString *domain2 = [val2 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + return CFStringCompare((CFStringRef)domain1, (CFStringRef)domain2, kCFCompareCaseInsensitive); +} + + +static void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) +{ + (void)store; // Unused + (void)changedKeys; // Unused + DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context; + assert(me != NULL); + + [me setupInitialValues]; +} + + +static void ServiceDomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context, DNSServiceFlags enumType) +{ + (void)sdRef; // Unused + (void)interfaceIndex; // Unused + (void)errorCode; // Unused + if (strcmp(replyDomain, "local.") == 0) return; // local domain is not interesting + + DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context; + BOOL moreComing = (BOOL)(flags & kDNSServiceFlagsMoreComing); + NSMutableArray * domainArray; + NSMutableArray * defaultBrowseDomainsArray = nil; + NSComboBox * domainComboBox; + NSString * domainString; + NSString * currentDomain = nil; + char decodedDomainString[kDNSServiceMaxDomainName] = "\0"; + char nextLabel[256] = "\0"; + char * buffer = (char *)replyDomain; + + while (*buffer) { + buffer = (char *)GetNextLabel(buffer, nextLabel); + strcat(decodedDomainString, nextLabel); + strcat(decodedDomainString, "."); + } + + // Remove trailing dot from domain name. + decodedDomainString[strlen(decodedDomainString)-1] = '\0'; + + domainString = [[[NSString alloc] initWithUTF8String:(const char *)decodedDomainString] autorelease]; + + if (enumType & kDNSServiceFlagsRegistrationDomains) { + domainArray = [me registrationDataSource]; + domainComboBox = [me regDomainsComboBox]; + currentDomain = [me currentRegDomain]; + } else { + domainArray = [me browseDataSource]; + domainComboBox = [me browseDomainsComboBox]; + defaultBrowseDomainsArray = [me defaultBrowseDomainsArray]; + } + + if (flags & kDNSServiceFlagsAdd) { + [domainArray removeObject:domainString]; // How can I check if an object is in the array? + [domainArray addObject:domainString]; + if ((flags & kDNSServiceFlagsDefault) && (enumType & kDNSServiceFlagsRegistrationDomains)) { + [me setDefaultRegDomain:domainString]; + if ([[domainComboBox stringValue] length] == 0) [domainComboBox setStringValue:domainString]; + } else if ((flags & kDNSServiceFlagsDefault) && !(enumType & kDNSServiceFlagsRegistrationDomains)) { + [defaultBrowseDomainsArray removeObject:domainString]; + [defaultBrowseDomainsArray addObject:domainString]; + } + } + + if (moreComing == NO) { + [domainArray sortUsingFunction:MyArrayCompareFunction context:nil]; + [domainComboBox reloadData]; + } +} + + +static void +browseDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context) +{ + ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsBrowseDomains); +} + + +static void +registrationDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context) +{ + ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsRegistrationDomains); +} + + + +static void +MyDNSServiceCleanUp(MyDNSServiceState * query) +{ + /* Remove the CFRunLoopSource from the current run loop. */ + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes); + CFRelease(query->source); + + /* Invalidate the CFSocket. */ + CFSocketInvalidate(query->socket); + CFRelease(query->socket); + + /* Workaround that gives time to CFSocket's select thread so it can remove the socket from its FD set + before we close the socket by calling DNSServiceRefDeallocate. <rdar://problem/3585273> */ + usleep(1000); + + /* Terminate the connection with the mDNSResponder daemon, which cancels the query. */ + DNSServiceRefDeallocate(query->service); +} + + + +static void +MySocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void * data, void * info) +{ + #pragma unused(s) + #pragma unused(type) + #pragma unused(address) + #pragma unused(data) + + DNSServiceErrorType err; + + MyDNSServiceState * query = (MyDNSServiceState *)info; // context passed in to CFSocketCreateWithNative(). + assert(query != NULL); + + /* Read a reply from the mDNSResponder. */ + err= DNSServiceProcessResult(query->service); + if (err != kDNSServiceErr_NoError) { + fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); + + /* Terminate the query operation and release the CFRunLoopSource and CFSocket. */ + MyDNSServiceCleanUp(query); + } +} + + + +static void +MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query) +{ + CFSocketNativeHandle sock; + CFOptionFlags sockFlags; + CFSocketContext context = { 0, query, NULL, NULL, NULL }; // Use MyDNSServiceState as context data. + + /* Access the underlying Unix domain socket to communicate with the mDNSResponder daemon. */ + sock = DNSServiceRefSockFD(query->service); + assert(sock != -1); + + /* Create a CFSocket using the Unix domain socket. */ + query->socket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, MySocketReadCallback, &context); + assert(query->socket != NULL); + + /* Prevent CFSocketInvalidate from closing DNSServiceRef's socket. */ + sockFlags = CFSocketGetSocketFlags(query->socket); + CFSocketSetSocketFlags(query->socket, sockFlags & (~kCFSocketCloseOnInvalidate)); + + /* Create a CFRunLoopSource from the CFSocket. */ + query->source = CFSocketCreateRunLoopSource(NULL, query->socket, 0); + assert(query->source != NULL); + + /* Add the CFRunLoopSource to the current run loop. */ + CFRunLoopAddSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes); +} + + + +-(void)updateStatusImageView +{ + int value = [self statusForHostName:currentHostName]; + if (value == 0) [statusImageView setImage:successImage]; + else if (value > 0) [statusImageView setImage:inprogressImage]; + else [statusImageView setImage:failureImage]; +} + + +- (void)watchForPreferenceChanges +{ + SCDynamicStoreContext context = { 0, self, NULL, NULL, NULL }; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("watchForPreferenceChanges"), NetworkChanged, &context); + CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFRunLoopSourceRef rls; + + assert(store != NULL); + assert(keys != NULL); + + CFArrayAppendValue(keys, SC_DYNDNS_STATE_KEY); + CFArrayAppendValue(keys, SC_DYNDNS_SETUP_KEY); + + (void)SCDynamicStoreSetNotificationKeys(store, keys, NULL); + + rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); + assert(rls != NULL); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes); + + CFRelease(keys); + CFRelease(store); +} + + +-(int)statusForHostName:(NSString * )domain +{ + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("statusForHostName"), NULL, NULL); + NSString *lowercaseDomain = [domain lowercaseString]; + int status = 1; + + assert(store != NULL); + + NSDictionary *dynamicDNS = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY); + if (dynamicDNS) { + NSDictionary *hostNames = [dynamicDNS objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY]; + NSDictionary *infoDict = [hostNames objectForKey:lowercaseDomain]; + if (infoDict) status = [[infoDict objectForKey:(NSString*)SC_DYNDNS_STATUS_KEY] intValue]; + CFRelease(dynamicDNS); + } + CFRelease(store); + + return status; +} + + +- (void)startDomainBrowsing +{ + DNSServiceFlags flags; + OSStatus err = noErr; + + flags = kDNSServiceFlagsRegistrationDomains; + err = DNSServiceEnumerateDomains(®Query.service, flags, 0, registrationDomainReply, (void *)self); + if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(®Query); + + flags = kDNSServiceFlagsBrowseDomains; + err = DNSServiceEnumerateDomains(&browseQuery.service, flags, 0, browseDomainReply, (void *)self); + if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(&browseQuery); +} + + +-(void)readPreferences +{ + NSDictionary *origDict; + NSArray *regDomainArray; + NSArray *hostArray; + + if (currentRegDomain) [currentRegDomain release]; + if (currentBrowseDomainsArray) [currentBrowseDomainsArray release]; + if (currentHostName) [currentHostName release]; + + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL); + origDict = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY); + + regDomainArray = [origDict objectForKey:(NSString *)SC_DYNDNS_REGDOMAINS_KEY]; + if (regDomainArray && [regDomainArray count] > 0) { + currentRegDomain = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy]; + currentWideAreaState = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue]; + } else { + currentRegDomain = [[NSString alloc] initWithString:@""]; + currentWideAreaState = NO; + } + + currentBrowseDomainsArray = [[origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY] retain]; + + hostArray = [origDict objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY]; + if (hostArray && [hostArray count] > 0) { + currentHostName = [[[hostArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy]; + } else { + currentHostName = [[NSString alloc] initWithString:@""]; + } + + [origDict release]; + CFRelease(store); +} + + +- (void)tableViewSelectionDidChange:(NSNotification *)notification +{ + [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]]; +} + + +- (void)setBrowseDomainsComboBox +{ + NSString * domain = nil; + + if ([defaultBrowseDomainsArray count] > 0) { + NSEnumerator * arrayEnumerator = [defaultBrowseDomainsArray objectEnumerator]; + while ((domain = [arrayEnumerator nextObject]) != NULL) { + if ([self domainAlreadyInList:domain] == NO) break; + } + } + if (domain) [browseDomainsComboBox setStringValue:domain]; + else [browseDomainsComboBox setStringValue:@""]; +} + + +- (IBAction)addBrowseDomainClicked:(id)sender +{ + [self setBrowseDomainsComboBox]; + + [NSApp beginSheet:addBrowseDomainWindow modalForWindow:mainWindow modalDelegate:self + didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:sender]; + + [browseDomainList deselectAll:sender]; + [self updateApplyButtonState]; +} + + +- (IBAction)removeBrowseDomainClicked:(id)sender +{ + (void)sender; // Unused + int selectedBrowseDomain = [browseDomainList selectedRow]; + [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain]; + [browseDomainList reloadData]; + [self updateApplyButtonState]; +} + + +- (IBAction)enableBrowseDomainClicked:(id)sender +{ + NSTableView *tableView = sender; + NSMutableDictionary *browseDomainDict; + int value; + + browseDomainDict = [[browseDomainsArray objectAtIndex:[tableView clickedRow]] mutableCopy]; + value = [[browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue]; + [browseDomainDict setObject:[[[NSNumber alloc] initWithInt:(!value)] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY]; + [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict]; + [tableView reloadData]; + [self updateApplyButtonState]; +} + + + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView +{ + (void)tableView; // Unused + int numberOfRows = 0; + + if (browseDomainsArray) { + numberOfRows = [browseDomainsArray count]; + } + return numberOfRows; +} + + +- (void)tabView:(NSTabView *)xtabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem +{ + (void)xtabView; // Unused + (void)tabViewItem; // Unused + [browseDomainList deselectAll:self]; + [mainWindow makeFirstResponder:nil]; +} + + +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row +{ + (void)tableView; // Unused + NSDictionary *browseDomainDict; + id value = nil; + + if (browseDomainsArray) { + browseDomainDict = [browseDomainsArray objectAtIndex:row]; + if (browseDomainDict) { + if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_ENABLED_KEY]) { + value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY]; + } else if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_DOMAIN_KEY]) { + value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + } + } + } + return value; +} + + +- (void)setupInitialValues +{ + [self readPreferences]; + + if (currentHostName) { + [hostName setStringValue:currentHostName]; + [self updateStatusImageView]; + } + + if (browseDomainsArray) { + [browseDomainsArray release]; + browseDomainsArray = nil; + } + + if (currentBrowseDomainsArray) { + browseDomainsArray = [currentBrowseDomainsArray mutableCopy]; + if (browseDomainsArray) { + [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil]; + if ([browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) { + OSStatus err = WriteBrowseDomain((CFDataRef)[self dataForDomainArray:browseDomainsArray]); + if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", (int32_t)err); + [currentBrowseDomainsArray release]; + currentBrowseDomainsArray = [browseDomainsArray copy]; + } + } + } else { + browseDomainsArray = nil; + } + [browseDomainList reloadData]; + + if (currentRegDomain && ([currentRegDomain length] > 0)) { + [regDomainsComboBox setStringValue:currentRegDomain]; + [registrationDataSource removeObject:currentRegDomain]; + [registrationDataSource addObject:currentRegDomain]; + [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil]; + [regDomainsComboBox reloadData]; + } + + if (currentWideAreaState) { + [self toggleWideAreaBonjour:YES]; + } else { + [self toggleWideAreaBonjour:NO]; + } + + if (hostNameSharedSecretValue) { + [hostNameSharedSecretValue release]; + hostNameSharedSecretValue = nil; + } + + if (regSharedSecretValue) { + [regSharedSecretValue release]; + regSharedSecretValue = nil; + } + + [self updateApplyButtonState]; + [mainWindow makeFirstResponder:nil]; + [browseDomainList deselectAll:self]; + [removeBrowseDomainButton setEnabled:NO]; +} + + + +- (void)awakeFromNib +{ + OSStatus err; + + prefsNeedUpdating = NO; + toolInstalled = NO; + browseDomainListEnabled = NO; + defaultRegDomain = nil; + currentRegDomain = nil; + currentBrowseDomainsArray = nil; + currentHostName = nil; + hostNameSharedSecretValue = nil; + regSharedSecretValue = nil; + browseDomainsArray = nil; + justStartedEditing = YES; + currentWideAreaState = NO; + NSString *successPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"success" ofType:@"tiff"]; + NSString *inprogressPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"inprogress" ofType:@"tiff"]; + NSString *failurePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"failure" ofType:@"tiff"]; + + registrationDataSource = [[NSMutableArray alloc] init]; + browseDataSource = [[NSMutableArray alloc] init]; + defaultBrowseDomainsArray = [[NSMutableArray alloc] init]; + successImage = [[NSImage alloc] initWithContentsOfFile:successPath]; + inprogressImage = [[NSImage alloc] initWithContentsOfFile:inprogressPath]; + failureImage = [[NSImage alloc] initWithContentsOfFile:failurePath]; + + [tabView selectFirstTabViewItem:self]; + [self setupInitialValues]; + [self startDomainBrowsing]; + [self watchForPreferenceChanges]; + + InitConfigAuthority(); + err = EnsureToolInstalled(); + if (err == noErr) toolInstalled = YES; + else { long int tmp = err; fprintf(stderr, "EnsureToolInstalled returned %ld\n", tmp); } + +} + + +- (IBAction)closeMyCustomSheet:(id)sender +{ + BOOL result = [sender isEqualTo:browseOKButton] || [sender isEqualTo:secretOKButton]; + + if (result) [NSApp endSheet:[sender window] returnCode:NSOKButton]; + else [NSApp endSheet:[sender window] returnCode:NSCancelButton]; +} + + +- (void)sharedSecretSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + NSButton * button = (NSButton *)contextInfo; + [sheet orderOut:self]; + [self enableControls]; + + if (returnCode == NSOKButton) { + if ([button isEqualTo:hostNameSharedSecretButton]) { + hostNameSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]]; + hostNameSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]]; + } else { + regSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]]; + regSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]]; + } + [self updateApplyButtonState]; + } + [sharedSecretValue setStringValue:@""]; +} + + +- (BOOL)domainAlreadyInList:(NSString *)domainString +{ + if (browseDomainsArray) { + NSDictionary *domainDict; + NSString *domainName; + NSEnumerator *arrayEnumerator = [browseDomainsArray objectEnumerator]; + while ((domainDict = [arrayEnumerator nextObject]) != NULL) { + domainName = [domainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + if ([domainString caseInsensitiveCompare:domainName] == NSOrderedSame) return YES; + } + } + return NO; +} + + +- (NSString *)trimCharactersFromDomain:(NSString *)domain +{ + NSMutableCharacterSet * trimSet = [[[NSCharacterSet whitespaceCharacterSet] mutableCopy] autorelease]; + [trimSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]]; + return [domain stringByTrimmingCharactersInSet:trimSet]; +} + + +- (void)addBrowseDomainSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + (void)contextInfo; // Unused + [sheet orderOut:self]; + [self enableControls]; + + if (returnCode == NSOKButton) { + NSString * newBrowseDomainString = [self trimCharactersFromDomain:[browseDomainsComboBox stringValue]]; + NSMutableDictionary *newBrowseDomainDict; + + if (browseDomainsArray == nil) browseDomainsArray = [[NSMutableArray alloc] initWithCapacity:0]; + if ([self domainAlreadyInList:newBrowseDomainString] == NO) { + newBrowseDomainDict = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease]; + + [newBrowseDomainDict setObject:newBrowseDomainString forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + [newBrowseDomainDict setObject:[[[NSNumber alloc] initWithBool:YES] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY]; + + [browseDomainsArray addObject:newBrowseDomainDict]; + [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil]; + [browseDomainList reloadData]; + [self updateApplyButtonState]; + } + } +} + + +-(void)validateTextFields +{ + [hostName validateEditing]; + [browseDomainsComboBox validateEditing]; + [regDomainsComboBox validateEditing]; +} + + +- (IBAction)changeButtonPressed:(id)sender +{ + NSString * keyName; + + [self disableControls]; + [self validateTextFields]; + [mainWindow makeFirstResponder:nil]; + [browseDomainList deselectAll:sender]; + + if ([sender isEqualTo:hostNameSharedSecretButton]) { + if (hostNameSharedSecretValue) { + [sharedSecretValue setStringValue:hostNameSharedSecretValue]; + } else if ((keyName = [self sharedSecretKeyName:[hostName stringValue]]) != NULL) { + [sharedSecretName setStringValue:keyName]; + [sharedSecretValue setStringValue:@"****************"]; + } else { + [sharedSecretName setStringValue:[hostName stringValue]]; + [sharedSecretValue setStringValue:@""]; + } + + } else { + if (regSharedSecretValue) { + [sharedSecretValue setStringValue:regSharedSecretValue]; + } else if ((keyName = [self sharedSecretKeyName:[regDomainsComboBox stringValue]]) != NULL) { + [sharedSecretName setStringValue:keyName]; + [sharedSecretValue setStringValue:@"****************"]; + } else { + [sharedSecretName setStringValue:[regDomainsComboBox stringValue]]; + [sharedSecretValue setStringValue:@""]; + } + } + + [sharedSecretWindow resignFirstResponder]; + + if ([[sharedSecretName stringValue] length] > 0) [sharedSecretWindow makeFirstResponder:sharedSecretValue]; + else [sharedSecretWindow makeFirstResponder:sharedSecretName]; + + [NSApp beginSheet:sharedSecretWindow modalForWindow:mainWindow modalDelegate:self + didEndSelector:@selector(sharedSecretSheetDidEnd:returnCode:contextInfo:) contextInfo:sender]; +} + + +- (IBAction)wideAreaCheckBoxChanged:(id)sender +{ + [self toggleWideAreaBonjour:[sender state]]; + [self updateApplyButtonState]; + [mainWindow makeFirstResponder:nil]; +} + + +- (void)updateApplyButtonState +{ + NSString *hostNameString = [hostName stringValue]; + NSString *regDomainString = [regDomainsComboBox stringValue]; + + NSComparisonResult hostNameResult = [hostNameString compare:currentHostName]; + NSComparisonResult regDomainResult = [regDomainString compare:currentRegDomain]; + + if ((currentHostName && (hostNameResult != NSOrderedSame)) || + (currentRegDomain && (regDomainResult != NSOrderedSame) && ([wideAreaCheckBox state])) || + (currentHostName == nil && ([hostNameString length]) > 0) || + (currentRegDomain == nil && ([regDomainString length]) > 0) || + (currentWideAreaState != [wideAreaCheckBox state]) || + (hostNameSharedSecretValue != nil) || + (regSharedSecretValue != nil) || + (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO)) + { + [self enableApplyButton]; + } else { + [self disableApplyButton]; + } +} + + + +- (void)controlTextDidChange:(NSNotification *)notification +{ + (void)notification; // Unused + [self updateApplyButtonState]; +} + + + +- (IBAction)comboAction:(id)sender +{ + (void)sender; // Unused + [self updateApplyButtonState]; +} + + +- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)ind +{ + NSString *domain = nil; + if ([aComboBox isEqualTo:browseDomainsComboBox]) domain = [browseDataSource objectAtIndex:ind]; + else if ([aComboBox isEqualTo:regDomainsComboBox]) domain = [registrationDataSource objectAtIndex:ind]; + return domain; +} + + + +- (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox +{ + int count = 0; + if ([aComboBox isEqualTo:browseDomainsComboBox]) count = [browseDataSource count]; + else if ([aComboBox isEqualTo:regDomainsComboBox]) count = [registrationDataSource count]; + return count; +} + + +- (NSMutableArray *)browseDataSource +{ + return browseDataSource; +} + + +- (NSMutableArray *)registrationDataSource +{ + return registrationDataSource; +} + + +- (NSComboBox *)browseDomainsComboBox +{ + return browseDomainsComboBox; +} + + +- (NSComboBox *)regDomainsComboBox +{ + return regDomainsComboBox; +} + + +- (NSString *)currentRegDomain +{ + return currentRegDomain; +} + + +- (NSMutableArray *)defaultBrowseDomainsArray +{ + return defaultBrowseDomainsArray; +} + + +- (NSArray *)currentBrowseDomainsArray +{ + return currentBrowseDomainsArray; +} + + +- (NSString *)currentHostName +{ + return currentHostName; +} + + +- (NSString *)defaultRegDomain +{ + return defaultRegDomain; +} + + +- (void)setDefaultRegDomain:(NSString *)domain +{ + [defaultRegDomain release]; + defaultRegDomain = domain; + [defaultRegDomain retain]; +} + + +- (void)didSelect +{ + [super didSelect]; + mainWindow = [[self mainView] window]; +} + + +- (void)mainViewDidLoad +{ + [comboAuthButton setString:"system.preferences"]; + [comboAuthButton setDelegate:self]; + [comboAuthButton updateStatus:nil]; + [comboAuthButton setAutoupdate:YES]; +} + + + +- (IBAction)applyClicked:(id)sender +{ + (void)sender; // Unused + [self applyCurrentState]; +} + + +- (void)applyCurrentState +{ + [self validateTextFields]; + + if (toolInstalled == YES) { + [self savePreferences]; + [self disableApplyButton]; + [mainWindow makeFirstResponder:nil]; + } +} + + +- (void)enableApplyButton +{ + [applyButton setEnabled:YES]; + [revertButton setEnabled:YES]; + prefsNeedUpdating = YES; +} + + +- (void)disableApplyButton +{ + [applyButton setEnabled:NO]; + [revertButton setEnabled:NO]; + prefsNeedUpdating = NO; +} + + +- (void)toggleWideAreaBonjour:(BOOL)state +{ + [wideAreaCheckBox setState:state]; + [regDomainsComboBox setEnabled:state]; + [registrationSharedSecretButton setEnabled:state]; +} + + +- (IBAction)revertClicked:(id)sender +{ + [self restorePreferences]; + [browseDomainList deselectAll:sender]; + [mainWindow makeFirstResponder:nil]; +} + + +- (void)restorePreferences +{ + [self setupInitialValues]; +} + + +- (void)savePanelWillClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + (void)sheet; // Unused + DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)contextInfo; + + if (returnCode == NSAlertDefaultReturn) { + [me applyCurrentState]; + } else if (returnCode == NSAlertAlternateReturn ) { + [me restorePreferences]; + } + + [me enableControls]; + [me replyToShouldUnselect:(returnCode != NSAlertOtherReturn)]; +} + + +-(SecKeychainItemRef)copyKeychainItemforDomain:(NSString *)domain +{ + const char * serviceName = [domain UTF8String]; + UInt32 type = 'ddns'; + UInt32 typeLength = sizeof(type); + + SecKeychainAttribute attrs[] = { { kSecServiceItemAttr, strlen(serviceName), (char *)serviceName }, + { kSecTypeItemAttr, typeLength, (UInt32 *)&type } }; + + SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs }; + SecKeychainSearchRef searchRef; + SecKeychainItemRef itemRef = NULL; + OSStatus err; + + err = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef); + if (err == noErr) { + err = SecKeychainSearchCopyNext(searchRef, &itemRef); + if (err != noErr) itemRef = NULL; + } + return itemRef; +} + + +-(NSString *)sharedSecretKeyName:(NSString * )domain +{ + SecKeychainItemRef itemRef = NULL; + NSString *keyName = nil; + OSStatus err; + + err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); + assert(err == noErr); + + itemRef = [self copyKeychainItemforDomain:[domain lowercaseString]]; + if (itemRef) { + UInt32 tags[1]; + SecKeychainAttributeInfo attrInfo; + SecKeychainAttributeList *attrList = NULL; + SecKeychainAttribute attribute; + unsigned int i; + + tags[0] = kSecAccountItemAttr; + attrInfo.count = 1; + attrInfo.tag = tags; + attrInfo.format = NULL; + + err = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, NULL, NULL); + if (err == noErr) { + for (i = 0; i < attrList->count; i++) { + attribute = attrList->attr[i]; + if (attribute.tag == kSecAccountItemAttr) { + keyName = [[NSString alloc] initWithBytes:attribute.data length:attribute.length encoding:NSUTF8StringEncoding]; + break; + } + } + if (attrList) (void)SecKeychainItemFreeAttributesAndData(attrList, NULL); + } + CFRelease(itemRef); + } + return keyName; +} + + +-(NSString *)domainForHostName:(NSString *)hostNameString +{ + NSString * domainName = nil; + char text[64]; + char * ptr = NULL; + + ptr = (char *)[hostNameString UTF8String]; + if (ptr) { + ptr = (char *)GetNextLabel(ptr, text); + domainName = [[NSString alloc] initWithUTF8String:(const char *)ptr]; + } + return ([domainName autorelease]); +} + + +- (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled +{ + NSMutableArray *domainsArray; + NSMutableDictionary *domainDict = nil; + + if (domainName && [domainName length] > 0) { + domainDict= [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease]; + [domainDict setObject:domainName forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + [domainDict setObject:[[[NSNumber alloc] initWithBool:enabled] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY]; + } + domainsArray = [[[NSMutableArray alloc] initWithCapacity:1] autorelease]; + if (domainDict) [domainsArray addObject:domainDict]; + return [NSArchiver archivedDataWithRootObject:domainsArray]; +} + + +- (NSData *)dataForDomainArray:(NSArray *)domainArray +{ + return [NSArchiver archivedDataWithRootObject:domainArray]; +} + + +- (NSData *)dataForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName +{ + NSMutableDictionary *sharedSecretDict = [[[NSMutableDictionary alloc] initWithCapacity:3] autorelease]; + [sharedSecretDict setObject:secret forKey:(NSString *)SC_DYNDNS_SECRET_KEY]; + [sharedSecretDict setObject:[domainName lowercaseString] forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + [sharedSecretDict setObject:keyName forKey:(NSString *)SC_DYNDNS_KEYNAME_KEY]; + return [NSArchiver archivedDataWithRootObject:sharedSecretDict]; +} + + +-(void)savePreferences +{ + NSString *hostNameString = [hostName stringValue]; + NSString *browseDomainString = [browseDomainsComboBox stringValue]; + NSString *regDomainString = [regDomainsComboBox stringValue]; + NSString *tempHostNameSharedSecretName = hostNameSharedSecretName; + NSString *tempRegSharedSecretName = regSharedSecretName; + NSData *browseDomainData = nil; + BOOL regSecretWasSet = NO; + BOOL hostSecretWasSet = NO; + OSStatus err = noErr; + + hostNameString = [self trimCharactersFromDomain:hostNameString]; + browseDomainString = [self trimCharactersFromDomain:browseDomainString]; + regDomainString = [self trimCharactersFromDomain:regDomainString]; + tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName]; + tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName]; + + [hostName setStringValue:hostNameString]; + [regDomainsComboBox setStringValue:regDomainString]; + + // Convert Shared Secret account names to lowercase. + tempHostNameSharedSecretName = [tempHostNameSharedSecretName lowercaseString]; + tempRegSharedSecretName = [tempRegSharedSecretName lowercaseString]; + + // Save hostname shared secret. + if ([hostNameSharedSecretName length] > 0 && ([hostNameSharedSecretValue length] > 0)) { + SetKeyForDomain((CFDataRef)[self dataForSharedSecret:hostNameSharedSecretValue domain:hostNameString key:tempHostNameSharedSecretName]); + [hostNameSharedSecretValue release]; + hostNameSharedSecretValue = nil; + hostSecretWasSet = YES; + } + + // Save registration domain shared secret. + if (([regSharedSecretName length] > 0) && ([regSharedSecretValue length] > 0)) { + SetKeyForDomain((CFDataRef)[self dataForSharedSecret:regSharedSecretValue domain:regDomainString key:tempRegSharedSecretName]); + [regSharedSecretValue release]; + regSharedSecretValue = nil; + regSecretWasSet = YES; + } + + // Save hostname. + if ((currentHostName == NULL) || [currentHostName compare:hostNameString] != NSOrderedSame) { + err = WriteHostname((CFDataRef)[self dataForDomain:hostNameString isEnabled:YES]); + if (err != noErr) NSLog(@"WriteHostname returned %d\n", (int32_t)err); + currentHostName = [hostNameString copy]; + } else if (hostSecretWasSet) { + WriteHostname((CFDataRef)[self dataForDomain:@"" isEnabled:NO]); + usleep(200000); // Temporary hack + if ([currentHostName length] > 0) WriteHostname((CFDataRef)[self dataForDomain:(NSString *)currentHostName isEnabled:YES]); + } + + // Save browse domain. + if (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) { + browseDomainData = [self dataForDomainArray:browseDomainsArray]; + err = WriteBrowseDomain((CFDataRef)browseDomainData); + if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", (int32_t)err); + currentBrowseDomainsArray = [browseDomainsArray copy]; + } + + // Save registration domain. + if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) { + + err = WriteRegistrationDomain((CFDataRef)[self dataForDomain:regDomainString isEnabled:[wideAreaCheckBox state]]); + if (err != noErr) NSLog(@"WriteRegistrationDomain returned %d\n", (int32_t)err); + + if (currentRegDomain) CFRelease(currentRegDomain); + currentRegDomain = [regDomainString copy]; + + if ([currentRegDomain length] > 0) { + currentWideAreaState = [wideAreaCheckBox state]; + [registrationDataSource removeObject:regDomainString]; + [registrationDataSource addObject:currentRegDomain]; + [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil]; + [regDomainsComboBox reloadData]; + } else { + currentWideAreaState = NO; + [self toggleWideAreaBonjour:NO]; + if (defaultRegDomain != nil) [regDomainsComboBox setStringValue:defaultRegDomain]; + } + } else if (regSecretWasSet) { + WriteRegistrationDomain((CFDataRef)[self dataForDomain:@"" isEnabled:NO]); + usleep(200000); // Temporary hack + if ([currentRegDomain length] > 0) WriteRegistrationDomain((CFDataRef)[self dataForDomain:currentRegDomain isEnabled:currentWideAreaState]); + } +} + + +- (NSPreferencePaneUnselectReply)shouldUnselect +{ +#if 1 + if (prefsNeedUpdating == YES) { + + [self disableControls]; + + NSBeginAlertSheet( + @"Apply Configuration Changes?", + @"Apply", + @"Don't Apply", + @"Cancel", + mainWindow, + self, + @selector( savePanelWillClose:returnCode:contextInfo: ), + NULL, + (void *) self, // sender, + @"" ); + return NSUnselectLater; + } +#endif + + return NSUnselectNow; +} + + +-(void)disableControls +{ + [hostName setEnabled:NO]; + [hostNameSharedSecretButton setEnabled:NO]; + [browseDomainsComboBox setEnabled:NO]; + [applyButton setEnabled:NO]; + [revertButton setEnabled:NO]; + [wideAreaCheckBox setEnabled:NO]; + [regDomainsComboBox setEnabled:NO]; + [registrationSharedSecretButton setEnabled:NO]; + [statusImageView setEnabled:NO]; + + browseDomainListEnabled = NO; + [browseDomainList deselectAll:self]; + [browseDomainList setEnabled:NO]; + + [addBrowseDomainButton setEnabled:NO]; + [removeBrowseDomainButton setEnabled:NO]; +} + + +- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row +{ + (void)row; // Unused + (void)tableView; // Unused + return browseDomainListEnabled; +} + + +-(void)enableControls +{ + [hostName setEnabled:YES]; + [hostNameSharedSecretButton setEnabled:YES]; + [browseDomainsComboBox setEnabled:YES]; + [wideAreaCheckBox setEnabled:YES]; + [registrationSharedSecretButton setEnabled:YES]; + [self toggleWideAreaBonjour:[wideAreaCheckBox state]]; + [statusImageView setEnabled:YES]; + [addBrowseDomainButton setEnabled:YES]; + + [browseDomainList setEnabled:YES]; + [browseDomainList deselectAll:self]; + browseDomainListEnabled = YES; + + [removeBrowseDomainButton setEnabled:[browseDomainList numberOfSelectedRows]]; + [applyButton setEnabled:prefsNeedUpdating]; + [revertButton setEnabled:prefsNeedUpdating]; +} + + +- (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view +{ + (void)view; // Unused + [self enableControls]; +} + + +- (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view +{ + (void)view; // Unused + [self disableControls]; +} + +@end + + +// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion +// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" +// To expand "version" to its value before making the string, use STRINGIFY(version) instead +#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s +#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) + +// NOT static -- otherwise the compiler may optimize it out +// The "@(#) " pattern is a special prefix the "what" command looks for +const char VersionString_SCCS[] = "@(#) Bonjour Preference Pane " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; + +#if _BUILDING_XCODE_PROJECT_ +// If the process crashes, then this string will be magically included in the automatically-generated crash log +const char *__crashreporter_info__ = VersionString_SCCS + 5; +asm(".desc ___crashreporter_info__, 0x10"); +#endif |