2 File: DNSServiceDiscoveryPref.m
4 Abstract: System Preference Pane for Dynamic DNS and Wide-Area DNS Service Discovery
6 Copyright: (c) Copyright 2005-2011 Apple Computer, Inc. All rights reserved.
8 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
9 ("Apple") in consideration of your agreement to the following terms, and your
10 use, installation, modification or redistribution of this Apple software
11 constitutes acceptance of these terms. If you do not agree with these terms,
12 please do not use, install, modify or redistribute this Apple software.
14 In consideration of your agreement to abide by the following terms, and subject
15 to these terms, Apple grants you a personal, non-exclusive license, under Apple's
16 copyrights in this original Apple software (the "Apple Software"), to use,
17 reproduce, modify and redistribute the Apple Software, with or without
18 modifications, in source and/or binary forms; provided that if you redistribute
19 the Apple Software in its entirety and without modifications, you must retain
20 this notice and the following text and disclaimers in all such redistributions of
21 the Apple Software. Neither the name, trademarks, service marks or logos of
22 Apple Computer, Inc. may be used to endorse or promote products derived from the
23 Apple Software without specific prior written permission from Apple. Except as
24 expressly stated in this notice, no other rights or licenses, express or implied,
25 are granted by Apple herein, including but not limited to any patent rights that
26 may be infringed by your derivative works or by other works in which the Apple
27 Software may be incorporated.
29 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
30 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
31 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
33 COMBINATION WITH YOUR PRODUCTS.
35 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
36 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
37 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
39 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
40 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
41 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 #import "DNSServiceDiscoveryPref.h"
45 #import "ConfigurationAuthority.h"
46 #import "PrivilegedOperations.h"
49 #include "../../Clients/ClientCommon.h"
51 #ifndef NSINTEGER_DEFINED
55 @implementation DNSServiceDiscoveryPref
57 static NSComparisonResult
58 MyArrayCompareFunction(id val1, id val2, void *context)
60 (void)context; // Unused
61 return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive);
65 static NSComparisonResult
66 MyDomainArrayCompareFunction(id val1, id val2, void *context)
68 (void)context; // Unused
69 NSString *domain1 = [val1 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
70 NSString *domain2 = [val2 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
71 return CFStringCompare((CFStringRef)domain1, (CFStringRef)domain2, kCFCompareCaseInsensitive);
75 static void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
77 (void)store; // Unused
78 (void)changedKeys; // Unused
79 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
82 [me setupInitialValues];
86 static void ServiceDomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
87 DNSServiceErrorType errorCode, const char *replyDomain, void *context, DNSServiceFlags enumType)
89 (void)sdRef; // Unused
90 (void)interfaceIndex; // Unused
91 (void)errorCode; // Unused
92 if (strcmp(replyDomain, "local.") == 0) return; // local domain is not interesting
94 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
95 BOOL moreComing = (BOOL)(flags & kDNSServiceFlagsMoreComing);
96 NSMutableArray * domainArray;
97 NSMutableArray * defaultBrowseDomainsArray = nil;
98 NSComboBox * domainComboBox;
99 NSString * domainString;
100 NSString * currentDomain = nil;
101 char decodedDomainString[kDNSServiceMaxDomainName] = "\0";
102 char nextLabel[256] = "\0";
103 char * buffer = (char *)replyDomain;
106 buffer = (char *)GetNextLabel(buffer, nextLabel);
107 strcat(decodedDomainString, nextLabel);
108 strcat(decodedDomainString, ".");
111 // Remove trailing dot from domain name.
112 decodedDomainString[strlen(decodedDomainString)-1] = '\0';
114 domainString = [[[NSString alloc] initWithUTF8String:(const char *)decodedDomainString] autorelease];
116 if (enumType & kDNSServiceFlagsRegistrationDomains) {
117 domainArray = [me registrationDataSource];
118 domainComboBox = [me regDomainsComboBox];
119 currentDomain = [me currentRegDomain];
121 domainArray = [me browseDataSource];
122 domainComboBox = [me browseDomainsComboBox];
123 defaultBrowseDomainsArray = [me defaultBrowseDomainsArray];
126 if (flags & kDNSServiceFlagsAdd) {
127 [domainArray removeObject:domainString]; // How can I check if an object is in the array?
128 [domainArray addObject:domainString];
129 if ((flags & kDNSServiceFlagsDefault) && (enumType & kDNSServiceFlagsRegistrationDomains)) {
130 [me setDefaultRegDomain:domainString];
131 if ([[domainComboBox stringValue] length] == 0) [domainComboBox setStringValue:domainString];
132 } else if ((flags & kDNSServiceFlagsDefault) && !(enumType & kDNSServiceFlagsRegistrationDomains)) {
133 [defaultBrowseDomainsArray removeObject:domainString];
134 [defaultBrowseDomainsArray addObject:domainString];
138 if (moreComing == NO) {
139 [domainArray sortUsingFunction:MyArrayCompareFunction context:nil];
140 [domainComboBox reloadData];
146 browseDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
147 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
149 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsBrowseDomains);
154 registrationDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
155 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
157 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsRegistrationDomains);
163 MyDNSServiceCleanUp(MyDNSServiceState * query)
165 /* Remove the CFRunLoopSource from the current run loop. */
166 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
167 CFRelease(query->source);
169 /* Invalidate the CFSocket. */
170 CFSocketInvalidate(query->socket);
171 CFRelease(query->socket);
173 /* Workaround that gives time to CFSocket's select thread so it can remove the socket from its FD set
174 before we close the socket by calling DNSServiceRefDeallocate. <rdar://problem/3585273> */
177 /* Terminate the connection with the mDNSResponder daemon, which cancels the query. */
178 DNSServiceRefDeallocate(query->service);
184 MySocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void * data, void * info)
188 #pragma unused(address)
191 DNSServiceErrorType err;
193 MyDNSServiceState * query = (MyDNSServiceState *)info; // context passed in to CFSocketCreateWithNative().
194 assert(query != NULL);
196 /* Read a reply from the mDNSResponder. */
197 err= DNSServiceProcessResult(query->service);
198 if (err != kDNSServiceErr_NoError) {
199 fprintf(stderr, "DNSServiceProcessResult returned %d\n", err);
201 /* Terminate the query operation and release the CFRunLoopSource and CFSocket. */
202 MyDNSServiceCleanUp(query);
209 MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query)
211 CFSocketNativeHandle sock;
212 CFOptionFlags sockFlags;
213 CFSocketContext context = { 0, query, NULL, NULL, NULL }; // Use MyDNSServiceState as context data.
215 /* Access the underlying Unix domain socket to communicate with the mDNSResponder daemon. */
216 sock = DNSServiceRefSockFD(query->service);
219 /* Create a CFSocket using the Unix domain socket. */
220 query->socket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, MySocketReadCallback, &context);
221 assert(query->socket != NULL);
223 /* Prevent CFSocketInvalidate from closing DNSServiceRef's socket. */
224 sockFlags = CFSocketGetSocketFlags(query->socket);
225 CFSocketSetSocketFlags(query->socket, sockFlags & (~kCFSocketCloseOnInvalidate));
227 /* Create a CFRunLoopSource from the CFSocket. */
228 query->source = CFSocketCreateRunLoopSource(NULL, query->socket, 0);
229 assert(query->source != NULL);
231 /* Add the CFRunLoopSource to the current run loop. */
232 CFRunLoopAddSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
237 -(void)updateStatusImageView
239 int value = [self statusForHostName:currentHostName];
240 if (value == 0) [statusImageView setImage:successImage];
241 else if (value > 0) [statusImageView setImage:inprogressImage];
242 else [statusImageView setImage:failureImage];
246 - (void)watchForPreferenceChanges
248 SCDynamicStoreContext context = { 0, self, NULL, NULL, NULL };
249 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("watchForPreferenceChanges"), NetworkChanged, &context);
250 CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
251 CFRunLoopSourceRef rls;
253 assert(store != NULL);
254 assert(keys != NULL);
256 CFArrayAppendValue(keys, SC_DYNDNS_STATE_KEY);
257 CFArrayAppendValue(keys, SC_DYNDNS_SETUP_KEY);
259 (void)SCDynamicStoreSetNotificationKeys(store, keys, NULL);
261 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
264 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
271 -(int)statusForHostName:(NSString * )domain
273 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("statusForHostName"), NULL, NULL);
274 NSString *lowercaseDomain = [domain lowercaseString];
277 assert(store != NULL);
279 NSDictionary *dynamicDNS = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY);
281 NSDictionary *hostNames = [dynamicDNS objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
282 NSDictionary *infoDict = [hostNames objectForKey:lowercaseDomain];
283 if (infoDict) status = [[infoDict objectForKey:(NSString*)SC_DYNDNS_STATUS_KEY] intValue];
284 CFRelease(dynamicDNS);
292 - (void)startDomainBrowsing
294 DNSServiceFlags flags;
295 OSStatus err = noErr;
297 flags = kDNSServiceFlagsRegistrationDomains;
298 err = DNSServiceEnumerateDomains(®Query.service, flags, 0, registrationDomainReply, (void *)self);
299 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(®Query);
301 flags = kDNSServiceFlagsBrowseDomains;
302 err = DNSServiceEnumerateDomains(&browseQuery.service, flags, 0, browseDomainReply, (void *)self);
303 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(&browseQuery);
307 -(void)readPreferences
309 NSDictionary *origDict;
310 NSArray *regDomainArray;
313 if (currentRegDomain) [currentRegDomain release];
314 if (currentBrowseDomainsArray) [currentBrowseDomainsArray release];
315 if (currentHostName) [currentHostName release];
317 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL);
318 origDict = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY);
320 regDomainArray = [origDict objectForKey:(NSString *)SC_DYNDNS_REGDOMAINS_KEY];
321 if (regDomainArray && [regDomainArray count] > 0) {
322 currentRegDomain = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
323 currentWideAreaState = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
325 currentRegDomain = [[NSString alloc] initWithString:@""];
326 currentWideAreaState = NO;
329 currentBrowseDomainsArray = [[origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY] retain];
331 hostArray = [origDict objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
332 if (hostArray && [hostArray count] > 0) {
333 currentHostName = [[[hostArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
335 currentHostName = [[NSString alloc] initWithString:@""];
343 - (void)tableViewSelectionDidChange:(NSNotification *)notification
345 [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]];
349 - (void)setBrowseDomainsComboBox
351 NSString * domain = nil;
353 if ([defaultBrowseDomainsArray count] > 0) {
354 NSEnumerator * arrayEnumerator = [defaultBrowseDomainsArray objectEnumerator];
355 while ((domain = [arrayEnumerator nextObject]) != NULL) {
356 if ([self domainAlreadyInList:domain] == NO) break;
359 if (domain) [browseDomainsComboBox setStringValue:domain];
360 else [browseDomainsComboBox setStringValue:@""];
364 - (IBAction)addBrowseDomainClicked:(id)sender
366 [self setBrowseDomainsComboBox];
368 [NSApp beginSheet:addBrowseDomainWindow modalForWindow:mainWindow modalDelegate:self
369 didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
371 [browseDomainList deselectAll:sender];
372 [self updateApplyButtonState];
376 - (IBAction)removeBrowseDomainClicked:(id)sender
378 (void)sender; // Unused
379 int selectedBrowseDomain = [browseDomainList selectedRow];
380 [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain];
381 [browseDomainList reloadData];
382 [self updateApplyButtonState];
386 - (IBAction)enableBrowseDomainClicked:(id)sender
388 NSTableView *tableView = sender;
389 NSMutableDictionary *browseDomainDict;
392 browseDomainDict = [[browseDomainsArray objectAtIndex:[tableView clickedRow]] mutableCopy];
393 value = [[browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
394 [browseDomainDict setObject:[[[NSNumber alloc] initWithInt:(!value)] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
395 [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict];
396 [tableView reloadData];
397 [self updateApplyButtonState];
402 - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
404 (void)tableView; // Unused
405 int numberOfRows = 0;
407 if (browseDomainsArray) {
408 numberOfRows = [browseDomainsArray count];
414 - (void)tabView:(NSTabView *)xtabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
416 (void)xtabView; // Unused
417 (void)tabViewItem; // Unused
418 [browseDomainList deselectAll:self];
419 [mainWindow makeFirstResponder:nil];
423 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
425 (void)tableView; // Unused
426 NSDictionary *browseDomainDict;
429 if (browseDomainsArray) {
430 browseDomainDict = [browseDomainsArray objectAtIndex:row];
431 if (browseDomainDict) {
432 if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_ENABLED_KEY]) {
433 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
434 } else if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_DOMAIN_KEY]) {
435 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
443 - (void)setupInitialValues
445 [self readPreferences];
447 if (currentHostName) {
448 [hostName setStringValue:currentHostName];
449 [self updateStatusImageView];
452 if (browseDomainsArray) {
453 [browseDomainsArray release];
454 browseDomainsArray = nil;
457 if (currentBrowseDomainsArray) {
458 browseDomainsArray = [currentBrowseDomainsArray mutableCopy];
459 if (browseDomainsArray) {
460 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
461 if ([browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
462 OSStatus err = WriteBrowseDomain((CFDataRef)[self dataForDomainArray:browseDomainsArray]);
463 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", (int32_t)err);
464 [currentBrowseDomainsArray release];
465 currentBrowseDomainsArray = [browseDomainsArray copy];
469 browseDomainsArray = nil;
471 [browseDomainList reloadData];
473 if (currentRegDomain && ([currentRegDomain length] > 0)) {
474 [regDomainsComboBox setStringValue:currentRegDomain];
475 [registrationDataSource removeObject:currentRegDomain];
476 [registrationDataSource addObject:currentRegDomain];
477 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
478 [regDomainsComboBox reloadData];
481 if (currentWideAreaState) {
482 [self toggleWideAreaBonjour:YES];
484 [self toggleWideAreaBonjour:NO];
487 if (hostNameSharedSecretValue) {
488 [hostNameSharedSecretValue release];
489 hostNameSharedSecretValue = nil;
492 if (regSharedSecretValue) {
493 [regSharedSecretValue release];
494 regSharedSecretValue = nil;
497 [self updateApplyButtonState];
498 [mainWindow makeFirstResponder:nil];
499 [browseDomainList deselectAll:self];
500 [removeBrowseDomainButton setEnabled:NO];
509 prefsNeedUpdating = NO;
511 browseDomainListEnabled = NO;
512 defaultRegDomain = nil;
513 currentRegDomain = nil;
514 currentBrowseDomainsArray = nil;
515 currentHostName = nil;
516 hostNameSharedSecretValue = nil;
517 regSharedSecretValue = nil;
518 browseDomainsArray = nil;
519 justStartedEditing = YES;
520 currentWideAreaState = NO;
521 NSString *successPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"success" ofType:@"tiff"];
522 NSString *inprogressPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"inprogress" ofType:@"tiff"];
523 NSString *failurePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"failure" ofType:@"tiff"];
525 registrationDataSource = [[NSMutableArray alloc] init];
526 browseDataSource = [[NSMutableArray alloc] init];
527 defaultBrowseDomainsArray = [[NSMutableArray alloc] init];
528 successImage = [[NSImage alloc] initWithContentsOfFile:successPath];
529 inprogressImage = [[NSImage alloc] initWithContentsOfFile:inprogressPath];
530 failureImage = [[NSImage alloc] initWithContentsOfFile:failurePath];
532 [tabView selectFirstTabViewItem:self];
533 [self setupInitialValues];
534 [self startDomainBrowsing];
535 [self watchForPreferenceChanges];
537 InitConfigAuthority();
538 err = EnsureToolInstalled();
539 if (err == noErr) toolInstalled = YES;
540 else { long int tmp = err; fprintf(stderr, "EnsureToolInstalled returned %ld\n", tmp); }
545 - (IBAction)closeMyCustomSheet:(id)sender
547 BOOL result = [sender isEqualTo:browseOKButton] || [sender isEqualTo:secretOKButton];
549 if (result) [NSApp endSheet:[sender window] returnCode:NSOKButton];
550 else [NSApp endSheet:[sender window] returnCode:NSCancelButton];
554 - (void)sharedSecretSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
556 NSButton * button = (NSButton *)contextInfo;
557 [sheet orderOut:self];
558 [self enableControls];
560 if (returnCode == NSOKButton) {
561 if ([button isEqualTo:hostNameSharedSecretButton]) {
562 hostNameSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
563 hostNameSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
565 regSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
566 regSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
568 [self updateApplyButtonState];
570 [sharedSecretValue setStringValue:@""];
574 - (BOOL)domainAlreadyInList:(NSString *)domainString
576 if (browseDomainsArray) {
577 NSDictionary *domainDict;
578 NSString *domainName;
579 NSEnumerator *arrayEnumerator = [browseDomainsArray objectEnumerator];
580 while ((domainDict = [arrayEnumerator nextObject]) != NULL) {
581 domainName = [domainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
582 if ([domainString caseInsensitiveCompare:domainName] == NSOrderedSame) return YES;
589 - (NSString *)trimCharactersFromDomain:(NSString *)domain
591 NSMutableCharacterSet * trimSet = [[[NSCharacterSet whitespaceCharacterSet] mutableCopy] autorelease];
592 [trimSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
593 return [domain stringByTrimmingCharactersInSet:trimSet];
597 - (void)addBrowseDomainSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
599 (void)contextInfo; // Unused
600 [sheet orderOut:self];
601 [self enableControls];
603 if (returnCode == NSOKButton) {
604 NSString * newBrowseDomainString = [self trimCharactersFromDomain:[browseDomainsComboBox stringValue]];
605 NSMutableDictionary *newBrowseDomainDict;
607 if (browseDomainsArray == nil) browseDomainsArray = [[NSMutableArray alloc] initWithCapacity:0];
608 if ([self domainAlreadyInList:newBrowseDomainString] == NO) {
609 newBrowseDomainDict = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
611 [newBrowseDomainDict setObject:newBrowseDomainString forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
612 [newBrowseDomainDict setObject:[[[NSNumber alloc] initWithBool:YES] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
614 [browseDomainsArray addObject:newBrowseDomainDict];
615 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
616 [browseDomainList reloadData];
617 [self updateApplyButtonState];
623 -(void)validateTextFields
625 [hostName validateEditing];
626 [browseDomainsComboBox validateEditing];
627 [regDomainsComboBox validateEditing];
631 - (IBAction)changeButtonPressed:(id)sender
635 [self disableControls];
636 [self validateTextFields];
637 [mainWindow makeFirstResponder:nil];
638 [browseDomainList deselectAll:sender];
640 if ([sender isEqualTo:hostNameSharedSecretButton]) {
641 if (hostNameSharedSecretValue) {
642 [sharedSecretValue setStringValue:hostNameSharedSecretValue];
643 } else if ((keyName = [self sharedSecretKeyName:[hostName stringValue]]) != NULL) {
644 [sharedSecretName setStringValue:keyName];
645 [sharedSecretValue setStringValue:@"****************"];
647 [sharedSecretName setStringValue:[hostName stringValue]];
648 [sharedSecretValue setStringValue:@""];
652 if (regSharedSecretValue) {
653 [sharedSecretValue setStringValue:regSharedSecretValue];
654 } else if ((keyName = [self sharedSecretKeyName:[regDomainsComboBox stringValue]]) != NULL) {
655 [sharedSecretName setStringValue:keyName];
656 [sharedSecretValue setStringValue:@"****************"];
658 [sharedSecretName setStringValue:[regDomainsComboBox stringValue]];
659 [sharedSecretValue setStringValue:@""];
663 [sharedSecretWindow resignFirstResponder];
665 if ([[sharedSecretName stringValue] length] > 0) [sharedSecretWindow makeFirstResponder:sharedSecretValue];
666 else [sharedSecretWindow makeFirstResponder:sharedSecretName];
668 [NSApp beginSheet:sharedSecretWindow modalForWindow:mainWindow modalDelegate:self
669 didEndSelector:@selector(sharedSecretSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
673 - (IBAction)wideAreaCheckBoxChanged:(id)sender
675 [self toggleWideAreaBonjour:[sender state]];
676 [self updateApplyButtonState];
677 [mainWindow makeFirstResponder:nil];
681 - (void)updateApplyButtonState
683 NSString *hostNameString = [hostName stringValue];
684 NSString *regDomainString = [regDomainsComboBox stringValue];
686 NSComparisonResult hostNameResult = [hostNameString compare:currentHostName];
687 NSComparisonResult regDomainResult = [regDomainString compare:currentRegDomain];
689 if ((currentHostName && (hostNameResult != NSOrderedSame)) ||
690 (currentRegDomain && (regDomainResult != NSOrderedSame) && ([wideAreaCheckBox state])) ||
691 (currentHostName == nil && ([hostNameString length]) > 0) ||
692 (currentRegDomain == nil && ([regDomainString length]) > 0) ||
693 (currentWideAreaState != [wideAreaCheckBox state]) ||
694 (hostNameSharedSecretValue != nil) ||
695 (regSharedSecretValue != nil) ||
696 (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO))
698 [self enableApplyButton];
700 [self disableApplyButton];
706 - (void)controlTextDidChange:(NSNotification *)notification
708 (void)notification; // Unused
709 [self updateApplyButtonState];
714 - (IBAction)comboAction:(id)sender
716 (void)sender; // Unused
717 [self updateApplyButtonState];
721 - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)ind
723 NSString *domain = nil;
724 if ([aComboBox isEqualTo:browseDomainsComboBox]) domain = [browseDataSource objectAtIndex:ind];
725 else if ([aComboBox isEqualTo:regDomainsComboBox]) domain = [registrationDataSource objectAtIndex:ind];
731 - (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox
734 if ([aComboBox isEqualTo:browseDomainsComboBox]) count = [browseDataSource count];
735 else if ([aComboBox isEqualTo:regDomainsComboBox]) count = [registrationDataSource count];
740 - (NSMutableArray *)browseDataSource
742 return browseDataSource;
746 - (NSMutableArray *)registrationDataSource
748 return registrationDataSource;
752 - (NSComboBox *)browseDomainsComboBox
754 return browseDomainsComboBox;
758 - (NSComboBox *)regDomainsComboBox
760 return regDomainsComboBox;
764 - (NSString *)currentRegDomain
766 return currentRegDomain;
770 - (NSMutableArray *)defaultBrowseDomainsArray
772 return defaultBrowseDomainsArray;
776 - (NSArray *)currentBrowseDomainsArray
778 return currentBrowseDomainsArray;
782 - (NSString *)currentHostName
784 return currentHostName;
788 - (NSString *)defaultRegDomain
790 return defaultRegDomain;
794 - (void)setDefaultRegDomain:(NSString *)domain
796 [defaultRegDomain release];
797 defaultRegDomain = domain;
798 [defaultRegDomain retain];
805 mainWindow = [[self mainView] window];
809 - (void)mainViewDidLoad
811 [comboAuthButton setString:"system.preferences"];
812 [comboAuthButton setDelegate:self];
813 [comboAuthButton updateStatus:nil];
814 [comboAuthButton setAutoupdate:YES];
819 - (IBAction)applyClicked:(id)sender
821 (void)sender; // Unused
822 [self applyCurrentState];
826 - (void)applyCurrentState
828 [self validateTextFields];
830 if (toolInstalled == YES) {
831 [self savePreferences];
832 [self disableApplyButton];
833 [mainWindow makeFirstResponder:nil];
838 - (void)enableApplyButton
840 [applyButton setEnabled:YES];
841 [revertButton setEnabled:YES];
842 prefsNeedUpdating = YES;
846 - (void)disableApplyButton
848 [applyButton setEnabled:NO];
849 [revertButton setEnabled:NO];
850 prefsNeedUpdating = NO;
854 - (void)toggleWideAreaBonjour:(BOOL)state
856 [wideAreaCheckBox setState:state];
857 [regDomainsComboBox setEnabled:state];
858 [registrationSharedSecretButton setEnabled:state];
862 - (IBAction)revertClicked:(id)sender
864 [self restorePreferences];
865 [browseDomainList deselectAll:sender];
866 [mainWindow makeFirstResponder:nil];
870 - (void)restorePreferences
872 [self setupInitialValues];
876 - (void)savePanelWillClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
878 (void)sheet; // Unused
879 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)contextInfo;
881 if (returnCode == NSAlertDefaultReturn) {
882 [me applyCurrentState];
883 } else if (returnCode == NSAlertAlternateReturn ) {
884 [me restorePreferences];
888 [me replyToShouldUnselect:(returnCode != NSAlertOtherReturn)];
892 -(SecKeychainItemRef)copyKeychainItemforDomain:(NSString *)domain
894 const char * serviceName = [domain UTF8String];
895 UInt32 type = 'ddns';
896 UInt32 typeLength = sizeof(type);
898 SecKeychainAttribute attrs[] = { { kSecServiceItemAttr, strlen(serviceName), (char *)serviceName },
899 { kSecTypeItemAttr, typeLength, (UInt32 *)&type } };
901 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
902 SecKeychainSearchRef searchRef;
903 SecKeychainItemRef itemRef = NULL;
906 err = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef);
908 err = SecKeychainSearchCopyNext(searchRef, &itemRef);
909 if (err != noErr) itemRef = NULL;
915 -(NSString *)sharedSecretKeyName:(NSString * )domain
917 SecKeychainItemRef itemRef = NULL;
918 NSString *keyName = nil;
921 err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
922 assert(err == noErr);
924 itemRef = [self copyKeychainItemforDomain:[domain lowercaseString]];
927 SecKeychainAttributeInfo attrInfo;
928 SecKeychainAttributeList *attrList = NULL;
929 SecKeychainAttribute attribute;
932 tags[0] = kSecAccountItemAttr;
935 attrInfo.format = NULL;
937 err = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, NULL, NULL);
939 for (i = 0; i < attrList->count; i++) {
940 attribute = attrList->attr[i];
941 if (attribute.tag == kSecAccountItemAttr) {
942 keyName = [[NSString alloc] initWithBytes:attribute.data length:attribute.length encoding:NSUTF8StringEncoding];
946 if (attrList) (void)SecKeychainItemFreeAttributesAndData(attrList, NULL);
954 -(NSString *)domainForHostName:(NSString *)hostNameString
956 NSString * domainName = nil;
960 ptr = (char *)[hostNameString UTF8String];
962 ptr = (char *)GetNextLabel(ptr, text);
963 domainName = [[NSString alloc] initWithUTF8String:(const char *)ptr];
965 return ([domainName autorelease]);
969 - (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled
971 NSMutableArray *domainsArray;
972 NSMutableDictionary *domainDict = nil;
974 if (domainName && [domainName length] > 0) {
975 domainDict= [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
976 [domainDict setObject:domainName forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
977 [domainDict setObject:[[[NSNumber alloc] initWithBool:enabled] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
979 domainsArray = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
980 if (domainDict) [domainsArray addObject:domainDict];
981 return [NSArchiver archivedDataWithRootObject:domainsArray];
985 - (NSData *)dataForDomainArray:(NSArray *)domainArray
987 return [NSArchiver archivedDataWithRootObject:domainArray];
991 - (NSData *)dataForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName
993 NSMutableDictionary *sharedSecretDict = [[[NSMutableDictionary alloc] initWithCapacity:3] autorelease];
994 [sharedSecretDict setObject:secret forKey:(NSString *)SC_DYNDNS_SECRET_KEY];
995 [sharedSecretDict setObject:[domainName lowercaseString] forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
996 [sharedSecretDict setObject:keyName forKey:(NSString *)SC_DYNDNS_KEYNAME_KEY];
997 return [NSArchiver archivedDataWithRootObject:sharedSecretDict];
1001 -(void)savePreferences
1003 NSString *hostNameString = [hostName stringValue];
1004 NSString *browseDomainString = [browseDomainsComboBox stringValue];
1005 NSString *regDomainString = [regDomainsComboBox stringValue];
1006 NSString *tempHostNameSharedSecretName = hostNameSharedSecretName;
1007 NSString *tempRegSharedSecretName = regSharedSecretName;
1008 NSData *browseDomainData = nil;
1009 BOOL regSecretWasSet = NO;
1010 BOOL hostSecretWasSet = NO;
1011 OSStatus err = noErr;
1013 hostNameString = [self trimCharactersFromDomain:hostNameString];
1014 browseDomainString = [self trimCharactersFromDomain:browseDomainString];
1015 regDomainString = [self trimCharactersFromDomain:regDomainString];
1016 tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName];
1017 tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName];
1019 [hostName setStringValue:hostNameString];
1020 [regDomainsComboBox setStringValue:regDomainString];
1022 // Convert Shared Secret account names to lowercase.
1023 tempHostNameSharedSecretName = [tempHostNameSharedSecretName lowercaseString];
1024 tempRegSharedSecretName = [tempRegSharedSecretName lowercaseString];
1026 // Save hostname shared secret.
1027 if ([hostNameSharedSecretName length] > 0 && ([hostNameSharedSecretValue length] > 0)) {
1028 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:hostNameSharedSecretValue domain:hostNameString key:tempHostNameSharedSecretName]);
1029 [hostNameSharedSecretValue release];
1030 hostNameSharedSecretValue = nil;
1031 hostSecretWasSet = YES;
1034 // Save registration domain shared secret.
1035 if (([regSharedSecretName length] > 0) && ([regSharedSecretValue length] > 0)) {
1036 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:regSharedSecretValue domain:regDomainString key:tempRegSharedSecretName]);
1037 [regSharedSecretValue release];
1038 regSharedSecretValue = nil;
1039 regSecretWasSet = YES;
1043 if ((currentHostName == NULL) || [currentHostName compare:hostNameString] != NSOrderedSame) {
1044 err = WriteHostname((CFDataRef)[self dataForDomain:hostNameString isEnabled:YES]);
1045 if (err != noErr) NSLog(@"WriteHostname returned %d\n", (int32_t)err);
1046 currentHostName = [hostNameString copy];
1047 } else if (hostSecretWasSet) {
1048 WriteHostname((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1049 usleep(200000); // Temporary hack
1050 if ([currentHostName length] > 0) WriteHostname((CFDataRef)[self dataForDomain:(NSString *)currentHostName isEnabled:YES]);
1053 // Save browse domain.
1054 if (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
1055 browseDomainData = [self dataForDomainArray:browseDomainsArray];
1056 err = WriteBrowseDomain((CFDataRef)browseDomainData);
1057 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", (int32_t)err);
1058 currentBrowseDomainsArray = [browseDomainsArray copy];
1061 // Save registration domain.
1062 if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) {
1064 err = WriteRegistrationDomain((CFDataRef)[self dataForDomain:regDomainString isEnabled:[wideAreaCheckBox state]]);
1065 if (err != noErr) NSLog(@"WriteRegistrationDomain returned %d\n", (int32_t)err);
1067 if (currentRegDomain) CFRelease(currentRegDomain);
1068 currentRegDomain = [regDomainString copy];
1070 if ([currentRegDomain length] > 0) {
1071 currentWideAreaState = [wideAreaCheckBox state];
1072 [registrationDataSource removeObject:regDomainString];
1073 [registrationDataSource addObject:currentRegDomain];
1074 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
1075 [regDomainsComboBox reloadData];
1077 currentWideAreaState = NO;
1078 [self toggleWideAreaBonjour:NO];
1079 if (defaultRegDomain != nil) [regDomainsComboBox setStringValue:defaultRegDomain];
1081 } else if (regSecretWasSet) {
1082 WriteRegistrationDomain((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1083 usleep(200000); // Temporary hack
1084 if ([currentRegDomain length] > 0) WriteRegistrationDomain((CFDataRef)[self dataForDomain:currentRegDomain isEnabled:currentWideAreaState]);
1089 - (NSPreferencePaneUnselectReply)shouldUnselect
1092 if (prefsNeedUpdating == YES) {
1094 [self disableControls];
1097 @"Apply Configuration Changes?",
1103 @selector( savePanelWillClose:returnCode:contextInfo: ),
1105 (void *) self, // sender,
1107 return NSUnselectLater;
1111 return NSUnselectNow;
1115 -(void)disableControls
1117 [hostName setEnabled:NO];
1118 [hostNameSharedSecretButton setEnabled:NO];
1119 [browseDomainsComboBox setEnabled:NO];
1120 [applyButton setEnabled:NO];
1121 [revertButton setEnabled:NO];
1122 [wideAreaCheckBox setEnabled:NO];
1123 [regDomainsComboBox setEnabled:NO];
1124 [registrationSharedSecretButton setEnabled:NO];
1125 [statusImageView setEnabled:NO];
1127 browseDomainListEnabled = NO;
1128 [browseDomainList deselectAll:self];
1129 [browseDomainList setEnabled:NO];
1131 [addBrowseDomainButton setEnabled:NO];
1132 [removeBrowseDomainButton setEnabled:NO];
1136 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row
1138 (void)row; // Unused
1139 (void)tableView; // Unused
1140 return browseDomainListEnabled;
1144 -(void)enableControls
1146 [hostName setEnabled:YES];
1147 [hostNameSharedSecretButton setEnabled:YES];
1148 [browseDomainsComboBox setEnabled:YES];
1149 [wideAreaCheckBox setEnabled:YES];
1150 [registrationSharedSecretButton setEnabled:YES];
1151 [self toggleWideAreaBonjour:[wideAreaCheckBox state]];
1152 [statusImageView setEnabled:YES];
1153 [addBrowseDomainButton setEnabled:YES];
1155 [browseDomainList setEnabled:YES];
1156 [browseDomainList deselectAll:self];
1157 browseDomainListEnabled = YES;
1159 [removeBrowseDomainButton setEnabled:[browseDomainList numberOfSelectedRows]];
1160 [applyButton setEnabled:prefsNeedUpdating];
1161 [revertButton setEnabled:prefsNeedUpdating];
1165 - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view
1167 (void)view; // Unused
1168 [self enableControls];
1172 - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view
1174 (void)view; // Unused
1175 [self disableControls];
1181 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
1182 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
1183 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
1184 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
1185 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
1187 // NOT static -- otherwise the compiler may optimize it out
1188 // The "@(#) " pattern is a special prefix the "what" command looks for
1189 const char VersionString_SCCS[] = "@(#) Bonjour Preference Pane " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
1191 #if _BUILDING_XCODE_PROJECT_
1192 // If the process crashes, then this string will be magically included in the automatically-generated crash log
1193 const char *__crashreporter_info__ = VersionString_SCCS + 5;
1194 asm(".desc ___crashreporter_info__, 0x10");