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
58 MyArrayCompareFunction(id val1, id val2, void *context)
60 (void)context; // Unused
61 return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive);
65 MyDomainArrayCompareFunction(id val1, id val2, void *context)
67 (void)context; // Unused
68 NSString *domain1 = [val1 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
69 NSString *domain2 = [val2 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
70 return CFStringCompare((CFStringRef)domain1, (CFStringRef)domain2, kCFCompareCaseInsensitive);
74 static void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
76 (void)store; // Unused
77 (void)changedKeys; // Unused
78 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
81 [me setupInitialValues];
85 static void ServiceDomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
86 DNSServiceErrorType errorCode, const char *replyDomain, void *context, DNSServiceFlags enumType)
88 (void)sdRef; // Unused
89 (void)interfaceIndex; // Unused
90 (void)errorCode; // Unused
91 if (strcmp(replyDomain, "local.") == 0) return; // local domain is not interesting
93 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
94 BOOL moreComing = (BOOL)(flags & kDNSServiceFlagsMoreComing);
95 NSMutableArray * domainArray;
96 NSMutableArray * defaultBrowseDomainsArray = nil;
97 NSComboBox * domainComboBox;
98 NSString * domainString;
99 NSString * currentDomain = nil;
100 char decodedDomainString[kDNSServiceMaxDomainName] = "\0";
101 char nextLabel[256] = "\0";
102 char * buffer = (char *)replyDomain;
105 buffer = (char *)GetNextLabel(buffer, nextLabel);
106 strcat(decodedDomainString, nextLabel);
107 strcat(decodedDomainString, ".");
110 // Remove trailing dot from domain name.
111 decodedDomainString[strlen(decodedDomainString)-1] = '\0';
113 domainString = [[[NSString alloc] initWithUTF8String:(const char *)decodedDomainString] autorelease];
115 if (enumType & kDNSServiceFlagsRegistrationDomains) {
116 domainArray = [me registrationDataSource];
117 domainComboBox = [me regDomainsComboBox];
118 currentDomain = [me currentRegDomain];
120 domainArray = [me browseDataSource];
121 domainComboBox = [me browseDomainsComboBox];
122 defaultBrowseDomainsArray = [me defaultBrowseDomainsArray];
125 if (flags & kDNSServiceFlagsAdd) {
126 [domainArray removeObject:domainString]; // How can I check if an object is in the array?
127 [domainArray addObject:domainString];
128 if ((flags & kDNSServiceFlagsDefault) && (enumType & kDNSServiceFlagsRegistrationDomains)) {
129 [me setDefaultRegDomain:domainString];
130 if ([[domainComboBox stringValue] length] == 0) [domainComboBox setStringValue:domainString];
131 } else if ((flags & kDNSServiceFlagsDefault) && !(enumType & kDNSServiceFlagsRegistrationDomains)) {
132 [defaultBrowseDomainsArray removeObject:domainString];
133 [defaultBrowseDomainsArray addObject:domainString];
137 if (moreComing == NO) {
138 [domainArray sortUsingFunction:MyArrayCompareFunction context:nil];
139 [domainComboBox reloadData];
145 browseDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
146 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
148 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsBrowseDomains);
153 registrationDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
154 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
156 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsRegistrationDomains);
162 MyDNSServiceCleanUp(MyDNSServiceState * query)
164 /* Remove the CFRunLoopSource from the current run loop. */
165 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
166 CFRelease(query->source);
168 /* Invalidate the CFSocket. */
169 CFSocketInvalidate(query->socket);
170 CFRelease(query->socket);
172 /* Workaround that gives time to CFSocket's select thread so it can remove the socket from its FD set
173 before we close the socket by calling DNSServiceRefDeallocate. <rdar://problem/3585273> */
176 /* Terminate the connection with the mDNSResponder daemon, which cancels the query. */
177 DNSServiceRefDeallocate(query->service);
183 MySocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void * data, void * info)
187 #pragma unused(address)
190 DNSServiceErrorType err;
192 MyDNSServiceState * query = (MyDNSServiceState *)info; // context passed in to CFSocketCreateWithNative().
193 assert(query != NULL);
195 /* Read a reply from the mDNSResponder. */
196 err= DNSServiceProcessResult(query->service);
197 if (err != kDNSServiceErr_NoError) {
198 fprintf(stderr, "DNSServiceProcessResult returned %d\n", err);
200 /* Terminate the query operation and release the CFRunLoopSource and CFSocket. */
201 MyDNSServiceCleanUp(query);
208 MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query)
210 CFSocketNativeHandle sock;
211 CFOptionFlags sockFlags;
212 CFSocketContext context = { 0, query, NULL, NULL, NULL }; // Use MyDNSServiceState as context data.
214 /* Access the underlying Unix domain socket to communicate with the mDNSResponder daemon. */
215 sock = DNSServiceRefSockFD(query->service);
218 /* Create a CFSocket using the Unix domain socket. */
219 query->socket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, MySocketReadCallback, &context);
220 assert(query->socket != NULL);
222 /* Prevent CFSocketInvalidate from closing DNSServiceRef's socket. */
223 sockFlags = CFSocketGetSocketFlags(query->socket);
224 CFSocketSetSocketFlags(query->socket, sockFlags & (~kCFSocketCloseOnInvalidate));
226 /* Create a CFRunLoopSource from the CFSocket. */
227 query->source = CFSocketCreateRunLoopSource(NULL, query->socket, 0);
228 assert(query->source != NULL);
230 /* Add the CFRunLoopSource to the current run loop. */
231 CFRunLoopAddSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
236 -(void)updateStatusImageView
238 int value = [self statusForHostName:currentHostName];
239 if (value == 0) [statusImageView setImage:successImage];
240 else if (value > 0) [statusImageView setImage:inprogressImage];
241 else [statusImageView setImage:failureImage];
245 - (void)watchForPreferenceChanges
247 SCDynamicStoreContext context = { 0, self, NULL, NULL, NULL };
248 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("watchForPreferenceChanges"), NetworkChanged, &context);
249 CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
250 CFRunLoopSourceRef rls;
252 assert(store != NULL);
253 assert(keys != NULL);
255 CFArrayAppendValue(keys, SC_DYNDNS_STATE_KEY);
256 CFArrayAppendValue(keys, SC_DYNDNS_SETUP_KEY);
258 (void)SCDynamicStoreSetNotificationKeys(store, keys, NULL);
260 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
263 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
270 -(int)statusForHostName:(NSString * )domain
272 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("statusForHostName"), NULL, NULL);
273 NSString *lowercaseDomain = [domain lowercaseString];
276 assert(store != NULL);
278 NSDictionary *dynamicDNS = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY);
280 NSDictionary *hostNames = [dynamicDNS objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
281 NSDictionary *infoDict = [hostNames objectForKey:lowercaseDomain];
282 if (infoDict) status = [[infoDict objectForKey:(NSString*)SC_DYNDNS_STATUS_KEY] intValue];
283 CFRelease(dynamicDNS);
291 - (void)startDomainBrowsing
293 DNSServiceFlags flags;
294 OSStatus err = noErr;
296 flags = kDNSServiceFlagsRegistrationDomains;
297 err = DNSServiceEnumerateDomains(®Query.service, flags, 0, registrationDomainReply, (void *)self);
298 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(®Query);
300 flags = kDNSServiceFlagsBrowseDomains;
301 err = DNSServiceEnumerateDomains(&browseQuery.service, flags, 0, browseDomainReply, (void *)self);
302 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(&browseQuery);
306 -(void)readPreferences
308 NSDictionary *origDict;
309 NSArray *regDomainArray;
312 if (currentRegDomain) [currentRegDomain release];
313 if (currentBrowseDomainsArray) [currentBrowseDomainsArray release];
314 if (currentHostName) [currentHostName release];
316 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL);
317 origDict = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY);
319 regDomainArray = [origDict objectForKey:(NSString *)SC_DYNDNS_REGDOMAINS_KEY];
320 if (regDomainArray && [regDomainArray count] > 0) {
321 currentRegDomain = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
322 currentWideAreaState = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
324 currentRegDomain = [[NSString alloc] initWithString:@""];
325 currentWideAreaState = NO;
328 currentBrowseDomainsArray = [[origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY] retain];
330 hostArray = [origDict objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
331 if (hostArray && [hostArray count] > 0) {
332 currentHostName = [[[hostArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
334 currentHostName = [[NSString alloc] initWithString:@""];
342 - (void)tableViewSelectionDidChange:(NSNotification *)notification
344 [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]];
348 - (void)setBrowseDomainsComboBox
350 NSString * domain = nil;
352 if ([defaultBrowseDomainsArray count] > 0) {
353 NSEnumerator * arrayEnumerator = [defaultBrowseDomainsArray objectEnumerator];
354 while ((domain = [arrayEnumerator nextObject]) != NULL) {
355 if ([self domainAlreadyInList:domain] == NO) break;
358 if (domain) [browseDomainsComboBox setStringValue:domain];
359 else [browseDomainsComboBox setStringValue:@""];
363 - (IBAction)addBrowseDomainClicked:(id)sender
365 [self setBrowseDomainsComboBox];
367 [NSApp beginSheet:addBrowseDomainWindow modalForWindow:mainWindow modalDelegate:self
368 didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
370 [browseDomainList deselectAll:sender];
371 [self updateApplyButtonState];
375 - (IBAction)removeBrowseDomainClicked:(id)sender
377 (void)sender; // Unused
378 int selectedBrowseDomain = [browseDomainList selectedRow];
379 [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain];
380 [browseDomainList reloadData];
381 [self updateApplyButtonState];
385 - (IBAction)enableBrowseDomainClicked:(id)sender
387 NSTableView *tableView = sender;
388 NSMutableDictionary *browseDomainDict;
391 browseDomainDict = [[browseDomainsArray objectAtIndex:[tableView clickedRow]] mutableCopy];
392 value = [[browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
393 [browseDomainDict setObject:[[[NSNumber alloc] initWithInt:(!value)] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
394 [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict];
395 [tableView reloadData];
396 [self updateApplyButtonState];
401 - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
403 (void)tableView; // Unused
404 int numberOfRows = 0;
406 if (browseDomainsArray) {
407 numberOfRows = [browseDomainsArray count];
413 - (void)tabView:(NSTabView *)xtabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
415 (void)xtabView; // Unused
416 (void)tabViewItem; // Unused
417 [browseDomainList deselectAll:self];
418 [mainWindow makeFirstResponder:nil];
422 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
424 (void)tableView; // Unused
425 NSDictionary *browseDomainDict;
428 if (browseDomainsArray) {
429 browseDomainDict = [browseDomainsArray objectAtIndex:row];
430 if (browseDomainDict) {
431 if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_ENABLED_KEY]) {
432 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
433 } else if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_DOMAIN_KEY]) {
434 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
442 - (void)setupInitialValues
444 [self readPreferences];
446 if (currentHostName) {
447 [hostName setStringValue:currentHostName];
448 [self updateStatusImageView];
451 if (browseDomainsArray) {
452 [browseDomainsArray release];
453 browseDomainsArray = nil;
456 if (currentBrowseDomainsArray) {
457 browseDomainsArray = [currentBrowseDomainsArray mutableCopy];
458 if (browseDomainsArray) {
459 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
460 if ([browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
461 OSStatus err = WriteBrowseDomain((CFDataRef)[self dataForDomainArray:browseDomainsArray]);
462 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", (int32_t)err);
463 [currentBrowseDomainsArray release];
464 currentBrowseDomainsArray = [browseDomainsArray copy];
468 browseDomainsArray = nil;
470 [browseDomainList reloadData];
472 if (currentRegDomain && ([currentRegDomain length] > 0)) {
473 [regDomainsComboBox setStringValue:currentRegDomain];
474 [registrationDataSource removeObject:currentRegDomain];
475 [registrationDataSource addObject:currentRegDomain];
476 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
477 [regDomainsComboBox reloadData];
480 if (currentWideAreaState) {
481 [self toggleWideAreaBonjour:YES];
483 [self toggleWideAreaBonjour:NO];
486 if (hostNameSharedSecretValue) {
487 [hostNameSharedSecretValue release];
488 hostNameSharedSecretValue = nil;
491 if (regSharedSecretValue) {
492 [regSharedSecretValue release];
493 regSharedSecretValue = nil;
496 [self updateApplyButtonState];
497 [mainWindow makeFirstResponder:nil];
498 [browseDomainList deselectAll:self];
499 [removeBrowseDomainButton setEnabled:NO];
508 prefsNeedUpdating = NO;
510 browseDomainListEnabled = NO;
511 defaultRegDomain = nil;
512 currentRegDomain = nil;
513 currentBrowseDomainsArray = nil;
514 currentHostName = nil;
515 hostNameSharedSecretValue = nil;
516 regSharedSecretValue = nil;
517 browseDomainsArray = nil;
518 justStartedEditing = YES;
519 currentWideAreaState = NO;
520 NSString *successPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"success" ofType:@"tiff"];
521 NSString *inprogressPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"inprogress" ofType:@"tiff"];
522 NSString *failurePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"failure" ofType:@"tiff"];
524 registrationDataSource = [[NSMutableArray alloc] init];
525 browseDataSource = [[NSMutableArray alloc] init];
526 defaultBrowseDomainsArray = [[NSMutableArray alloc] init];
527 successImage = [[NSImage alloc] initWithContentsOfFile:successPath];
528 inprogressImage = [[NSImage alloc] initWithContentsOfFile:inprogressPath];
529 failureImage = [[NSImage alloc] initWithContentsOfFile:failurePath];
531 [tabView selectFirstTabViewItem:self];
532 [self setupInitialValues];
533 [self startDomainBrowsing];
534 [self watchForPreferenceChanges];
536 InitConfigAuthority();
537 err = EnsureToolInstalled();
538 if (err == noErr) toolInstalled = YES;
539 else { long int tmp = err; fprintf(stderr, "EnsureToolInstalled returned %ld\n", tmp); }
544 - (IBAction)closeMyCustomSheet:(id)sender
546 BOOL result = [sender isEqualTo:browseOKButton] || [sender isEqualTo:secretOKButton];
548 if (result) [NSApp endSheet:[sender window] returnCode:NSOKButton];
549 else [NSApp endSheet:[sender window] returnCode:NSCancelButton];
553 - (void)sharedSecretSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
555 NSButton * button = (NSButton *)contextInfo;
556 [sheet orderOut:self];
557 [self enableControls];
559 if (returnCode == NSOKButton) {
560 if ([button isEqualTo:hostNameSharedSecretButton]) {
561 hostNameSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
562 hostNameSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
564 regSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
565 regSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
567 [self updateApplyButtonState];
569 [sharedSecretValue setStringValue:@""];
573 - (BOOL)domainAlreadyInList:(NSString *)domainString
575 if (browseDomainsArray) {
576 NSDictionary *domainDict;
577 NSString *domainName;
578 NSEnumerator *arrayEnumerator = [browseDomainsArray objectEnumerator];
579 while ((domainDict = [arrayEnumerator nextObject]) != NULL) {
580 domainName = [domainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
581 if ([domainString caseInsensitiveCompare:domainName] == NSOrderedSame) return YES;
588 - (NSString *)trimCharactersFromDomain:(NSString *)domain
590 NSMutableCharacterSet * trimSet = [[[NSCharacterSet whitespaceCharacterSet] mutableCopy] autorelease];
591 [trimSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
592 return [domain stringByTrimmingCharactersInSet:trimSet];
596 - (void)addBrowseDomainSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
598 (void)contextInfo; // Unused
599 [sheet orderOut:self];
600 [self enableControls];
602 if (returnCode == NSOKButton) {
603 NSString * newBrowseDomainString = [self trimCharactersFromDomain:[browseDomainsComboBox stringValue]];
604 NSMutableDictionary *newBrowseDomainDict;
606 if (browseDomainsArray == nil) browseDomainsArray = [[NSMutableArray alloc] initWithCapacity:0];
607 if ([self domainAlreadyInList:newBrowseDomainString] == NO) {
608 newBrowseDomainDict = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
610 [newBrowseDomainDict setObject:newBrowseDomainString forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
611 [newBrowseDomainDict setObject:[[[NSNumber alloc] initWithBool:YES] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
613 [browseDomainsArray addObject:newBrowseDomainDict];
614 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
615 [browseDomainList reloadData];
616 [self updateApplyButtonState];
622 -(void)validateTextFields
624 [hostName validateEditing];
625 [browseDomainsComboBox validateEditing];
626 [regDomainsComboBox validateEditing];
630 - (IBAction)changeButtonPressed:(id)sender
634 [self disableControls];
635 [self validateTextFields];
636 [mainWindow makeFirstResponder:nil];
637 [browseDomainList deselectAll:sender];
639 if ([sender isEqualTo:hostNameSharedSecretButton]) {
640 if (hostNameSharedSecretValue) {
641 [sharedSecretValue setStringValue:hostNameSharedSecretValue];
642 } else if ((keyName = [self sharedSecretKeyName:[hostName stringValue]]) != NULL) {
643 [sharedSecretName setStringValue:keyName];
644 [sharedSecretValue setStringValue:@"****************"];
646 [sharedSecretName setStringValue:[hostName stringValue]];
647 [sharedSecretValue setStringValue:@""];
651 if (regSharedSecretValue) {
652 [sharedSecretValue setStringValue:regSharedSecretValue];
653 } else if ((keyName = [self sharedSecretKeyName:[regDomainsComboBox stringValue]]) != NULL) {
654 [sharedSecretName setStringValue:keyName];
655 [sharedSecretValue setStringValue:@"****************"];
657 [sharedSecretName setStringValue:[regDomainsComboBox stringValue]];
658 [sharedSecretValue setStringValue:@""];
662 [sharedSecretWindow resignFirstResponder];
664 if ([[sharedSecretName stringValue] length] > 0) [sharedSecretWindow makeFirstResponder:sharedSecretValue];
665 else [sharedSecretWindow makeFirstResponder:sharedSecretName];
667 [NSApp beginSheet:sharedSecretWindow modalForWindow:mainWindow modalDelegate:self
668 didEndSelector:@selector(sharedSecretSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
672 - (IBAction)wideAreaCheckBoxChanged:(id)sender
674 [self toggleWideAreaBonjour:[sender state]];
675 [self updateApplyButtonState];
676 [mainWindow makeFirstResponder:nil];
680 - (void)updateApplyButtonState
682 NSString *hostNameString = [hostName stringValue];
683 NSString *regDomainString = [regDomainsComboBox stringValue];
685 NSComparisonResult hostNameResult = [hostNameString compare:currentHostName];
686 NSComparisonResult regDomainResult = [regDomainString compare:currentRegDomain];
688 if ((currentHostName && (hostNameResult != NSOrderedSame)) ||
689 (currentRegDomain && (regDomainResult != NSOrderedSame) && ([wideAreaCheckBox state])) ||
690 (currentHostName == nil && ([hostNameString length]) > 0) ||
691 (currentRegDomain == nil && ([regDomainString length]) > 0) ||
692 (currentWideAreaState != [wideAreaCheckBox state]) ||
693 (hostNameSharedSecretValue != nil) ||
694 (regSharedSecretValue != nil) ||
695 (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO))
697 [self enableApplyButton];
699 [self disableApplyButton];
705 - (void)controlTextDidChange:(NSNotification *)notification
707 (void)notification; // Unused
708 [self updateApplyButtonState];
713 - (IBAction)comboAction:(id)sender
715 (void)sender; // Unused
716 [self updateApplyButtonState];
720 - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)ind
722 NSString *domain = nil;
723 if ([aComboBox isEqualTo:browseDomainsComboBox]) domain = [browseDataSource objectAtIndex:ind];
724 else if ([aComboBox isEqualTo:regDomainsComboBox]) domain = [registrationDataSource objectAtIndex:ind];
730 - (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox
733 if ([aComboBox isEqualTo:browseDomainsComboBox]) count = [browseDataSource count];
734 else if ([aComboBox isEqualTo:regDomainsComboBox]) count = [registrationDataSource count];
739 - (NSMutableArray *)browseDataSource
741 return browseDataSource;
745 - (NSMutableArray *)registrationDataSource
747 return registrationDataSource;
751 - (NSComboBox *)browseDomainsComboBox
753 return browseDomainsComboBox;
757 - (NSComboBox *)regDomainsComboBox
759 return regDomainsComboBox;
763 - (NSString *)currentRegDomain
765 return currentRegDomain;
769 - (NSMutableArray *)defaultBrowseDomainsArray
771 return defaultBrowseDomainsArray;
775 - (NSArray *)currentBrowseDomainsArray
777 return currentBrowseDomainsArray;
781 - (NSString *)currentHostName
783 return currentHostName;
787 - (NSString *)defaultRegDomain
789 return defaultRegDomain;
793 - (void)setDefaultRegDomain:(NSString *)domain
795 [defaultRegDomain release];
796 defaultRegDomain = domain;
797 [defaultRegDomain retain];
804 mainWindow = [[self mainView] window];
808 - (void)mainViewDidLoad
810 [comboAuthButton setString:"system.preferences"];
811 [comboAuthButton setDelegate:self];
812 [comboAuthButton updateStatus:nil];
813 [comboAuthButton setAutoupdate:YES];
818 - (IBAction)applyClicked:(id)sender
820 (void)sender; // Unused
821 [self applyCurrentState];
825 - (void)applyCurrentState
827 [self validateTextFields];
829 if (toolInstalled == YES) {
830 [self savePreferences];
831 [self disableApplyButton];
832 [mainWindow makeFirstResponder:nil];
837 - (void)enableApplyButton
839 [applyButton setEnabled:YES];
840 [revertButton setEnabled:YES];
841 prefsNeedUpdating = YES;
845 - (void)disableApplyButton
847 [applyButton setEnabled:NO];
848 [revertButton setEnabled:NO];
849 prefsNeedUpdating = NO;
853 - (void)toggleWideAreaBonjour:(BOOL)state
855 [wideAreaCheckBox setState:state];
856 [regDomainsComboBox setEnabled:state];
857 [registrationSharedSecretButton setEnabled:state];
861 - (IBAction)revertClicked:(id)sender
863 [self restorePreferences];
864 [browseDomainList deselectAll:sender];
865 [mainWindow makeFirstResponder:nil];
869 - (void)restorePreferences
871 [self setupInitialValues];
875 - (void)savePanelWillClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
877 (void)sheet; // Unused
878 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)contextInfo;
880 if (returnCode == NSAlertDefaultReturn) {
881 [me applyCurrentState];
882 } else if (returnCode == NSAlertAlternateReturn ) {
883 [me restorePreferences];
887 [me replyToShouldUnselect:(returnCode != NSAlertOtherReturn)];
891 -(SecKeychainItemRef)copyKeychainItemforDomain:(NSString *)domain
893 const char * serviceName = [domain UTF8String];
894 UInt32 type = 'ddns';
895 UInt32 typeLength = sizeof(type);
897 SecKeychainAttribute attrs[] = { { kSecServiceItemAttr, strlen(serviceName), (char *)serviceName },
898 { kSecTypeItemAttr, typeLength, (UInt32 *)&type } };
900 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
901 SecKeychainSearchRef searchRef;
902 SecKeychainItemRef itemRef = NULL;
905 err = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef);
907 err = SecKeychainSearchCopyNext(searchRef, &itemRef);
908 if (err != noErr) itemRef = NULL;
914 -(NSString *)sharedSecretKeyName:(NSString * )domain
916 SecKeychainItemRef itemRef = NULL;
917 NSString *keyName = nil;
920 err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
921 assert(err == noErr);
923 itemRef = [self copyKeychainItemforDomain:[domain lowercaseString]];
926 SecKeychainAttributeInfo attrInfo;
927 SecKeychainAttributeList *attrList = NULL;
928 SecKeychainAttribute attribute;
931 tags[0] = kSecAccountItemAttr;
934 attrInfo.format = NULL;
936 err = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, NULL, NULL);
938 for (i = 0; i < attrList->count; i++) {
939 attribute = attrList->attr[i];
940 if (attribute.tag == kSecAccountItemAttr) {
941 keyName = [[NSString alloc] initWithBytes:attribute.data length:attribute.length encoding:NSUTF8StringEncoding];
945 if (attrList) (void)SecKeychainItemFreeAttributesAndData(attrList, NULL);
953 -(NSString *)domainForHostName:(NSString *)hostNameString
955 NSString * domainName = nil;
959 ptr = (char *)[hostNameString UTF8String];
961 ptr = (char *)GetNextLabel(ptr, text);
962 domainName = [[NSString alloc] initWithUTF8String:(const char *)ptr];
964 return ([domainName autorelease]);
968 - (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled
970 NSMutableArray *domainsArray;
971 NSMutableDictionary *domainDict = nil;
973 if (domainName && [domainName length] > 0) {
974 domainDict= [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
975 [domainDict setObject:domainName forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
976 [domainDict setObject:[[[NSNumber alloc] initWithBool:enabled] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
978 domainsArray = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
979 if (domainDict) [domainsArray addObject:domainDict];
980 return [NSArchiver archivedDataWithRootObject:domainsArray];
984 - (NSData *)dataForDomainArray:(NSArray *)domainArray
986 return [NSArchiver archivedDataWithRootObject:domainArray];
990 - (NSData *)dataForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName
992 NSMutableDictionary *sharedSecretDict = [[[NSMutableDictionary alloc] initWithCapacity:3] autorelease];
993 [sharedSecretDict setObject:secret forKey:(NSString *)SC_DYNDNS_SECRET_KEY];
994 [sharedSecretDict setObject:[domainName lowercaseString] forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
995 [sharedSecretDict setObject:keyName forKey:(NSString *)SC_DYNDNS_KEYNAME_KEY];
996 return [NSArchiver archivedDataWithRootObject:sharedSecretDict];
1000 -(void)savePreferences
1002 NSString *hostNameString = [hostName stringValue];
1003 NSString *browseDomainString = [browseDomainsComboBox stringValue];
1004 NSString *regDomainString = [regDomainsComboBox stringValue];
1005 NSString *tempHostNameSharedSecretName = hostNameSharedSecretName;
1006 NSString *tempRegSharedSecretName = regSharedSecretName;
1007 NSData *browseDomainData = nil;
1008 BOOL regSecretWasSet = NO;
1009 BOOL hostSecretWasSet = NO;
1010 OSStatus err = noErr;
1012 hostNameString = [self trimCharactersFromDomain:hostNameString];
1013 browseDomainString = [self trimCharactersFromDomain:browseDomainString];
1014 regDomainString = [self trimCharactersFromDomain:regDomainString];
1015 tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName];
1016 tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName];
1018 [hostName setStringValue:hostNameString];
1019 [regDomainsComboBox setStringValue:regDomainString];
1021 // Convert Shared Secret account names to lowercase.
1022 tempHostNameSharedSecretName = [tempHostNameSharedSecretName lowercaseString];
1023 tempRegSharedSecretName = [tempRegSharedSecretName lowercaseString];
1025 // Save hostname shared secret.
1026 if ([hostNameSharedSecretName length] > 0 && ([hostNameSharedSecretValue length] > 0)) {
1027 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:hostNameSharedSecretValue domain:hostNameString key:tempHostNameSharedSecretName]);
1028 [hostNameSharedSecretValue release];
1029 hostNameSharedSecretValue = nil;
1030 hostSecretWasSet = YES;
1033 // Save registration domain shared secret.
1034 if (([regSharedSecretName length] > 0) && ([regSharedSecretValue length] > 0)) {
1035 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:regSharedSecretValue domain:regDomainString key:tempRegSharedSecretName]);
1036 [regSharedSecretValue release];
1037 regSharedSecretValue = nil;
1038 regSecretWasSet = YES;
1042 if ((currentHostName == NULL) || [currentHostName compare:hostNameString] != NSOrderedSame) {
1043 err = WriteHostname((CFDataRef)[self dataForDomain:hostNameString isEnabled:YES]);
1044 if (err != noErr) NSLog(@"WriteHostname returned %d\n", (int32_t)err);
1045 currentHostName = [hostNameString copy];
1046 } else if (hostSecretWasSet) {
1047 WriteHostname((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1048 usleep(200000); // Temporary hack
1049 if ([currentHostName length] > 0) WriteHostname((CFDataRef)[self dataForDomain:(NSString *)currentHostName isEnabled:YES]);
1052 // Save browse domain.
1053 if (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
1054 browseDomainData = [self dataForDomainArray:browseDomainsArray];
1055 err = WriteBrowseDomain((CFDataRef)browseDomainData);
1056 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", (int32_t)err);
1057 currentBrowseDomainsArray = [browseDomainsArray copy];
1060 // Save registration domain.
1061 if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) {
1063 err = WriteRegistrationDomain((CFDataRef)[self dataForDomain:regDomainString isEnabled:[wideAreaCheckBox state]]);
1064 if (err != noErr) NSLog(@"WriteRegistrationDomain returned %d\n", (int32_t)err);
1066 if (currentRegDomain) CFRelease(currentRegDomain);
1067 currentRegDomain = [regDomainString copy];
1069 if ([currentRegDomain length] > 0) {
1070 currentWideAreaState = [wideAreaCheckBox state];
1071 [registrationDataSource removeObject:regDomainString];
1072 [registrationDataSource addObject:currentRegDomain];
1073 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
1074 [regDomainsComboBox reloadData];
1076 currentWideAreaState = NO;
1077 [self toggleWideAreaBonjour:NO];
1078 if (defaultRegDomain != nil) [regDomainsComboBox setStringValue:defaultRegDomain];
1080 } else if (regSecretWasSet) {
1081 WriteRegistrationDomain((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1082 usleep(200000); // Temporary hack
1083 if ([currentRegDomain length] > 0) WriteRegistrationDomain((CFDataRef)[self dataForDomain:currentRegDomain isEnabled:currentWideAreaState]);
1088 - (NSPreferencePaneUnselectReply)shouldUnselect
1091 if (prefsNeedUpdating == YES) {
1093 [self disableControls];
1096 @"Apply Configuration Changes?",
1102 @selector( savePanelWillClose:returnCode:contextInfo: ),
1104 (void *) self, // sender,
1106 return NSUnselectLater;
1110 return NSUnselectNow;
1114 -(void)disableControls
1116 [hostName setEnabled:NO];
1117 [hostNameSharedSecretButton setEnabled:NO];
1118 [browseDomainsComboBox setEnabled:NO];
1119 [applyButton setEnabled:NO];
1120 [revertButton setEnabled:NO];
1121 [wideAreaCheckBox setEnabled:NO];
1122 [regDomainsComboBox setEnabled:NO];
1123 [registrationSharedSecretButton setEnabled:NO];
1124 [statusImageView setEnabled:NO];
1126 browseDomainListEnabled = NO;
1127 [browseDomainList deselectAll:self];
1128 [browseDomainList setEnabled:NO];
1130 [addBrowseDomainButton setEnabled:NO];
1131 [removeBrowseDomainButton setEnabled:NO];
1135 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row
1137 (void)row; // Unused
1138 (void)tableView; // Unused
1139 return browseDomainListEnabled;
1143 -(void)enableControls
1145 [hostName setEnabled:YES];
1146 [hostNameSharedSecretButton setEnabled:YES];
1147 [browseDomainsComboBox setEnabled:YES];
1148 [wideAreaCheckBox setEnabled:YES];
1149 [registrationSharedSecretButton setEnabled:YES];
1150 [self toggleWideAreaBonjour:[wideAreaCheckBox state]];
1151 [statusImageView setEnabled:YES];
1152 [addBrowseDomainButton setEnabled:YES];
1154 [browseDomainList setEnabled:YES];
1155 [browseDomainList deselectAll:self];
1156 browseDomainListEnabled = YES;
1158 [removeBrowseDomainButton setEnabled:[browseDomainList numberOfSelectedRows]];
1159 [applyButton setEnabled:prefsNeedUpdating];
1160 [revertButton setEnabled:prefsNeedUpdating];
1164 - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view
1166 (void)view; // Unused
1167 [self enableControls];
1171 - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view
1173 (void)view; // Unused
1174 [self disableControls];
1180 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
1181 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
1182 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
1183 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
1184 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
1186 // NOT static -- otherwise the compiler may optimize it out
1187 // The "@(#) " pattern is a special prefix the "what" command looks for
1188 const char VersionString_SCCS[] = "@(#) Bonjour Preference Pane " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
1190 #if _BUILDING_XCODE_PROJECT_
1191 // If the process crashes, then this string will be magically included in the automatically-generated crash log
1192 const char *__crashreporter_info__ = VersionString_SCCS + 5;
1193 asm(".desc ___crashreporter_info__, 0x10");