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 Inc. All rights reserved.
8 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 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 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 char decodedDomainString[kDNSServiceMaxDomainName] = "\0";
100 char nextLabel[256] = "\0";
101 char * buffer = (char *)replyDomain;
104 buffer = (char *)GetNextLabel(buffer, nextLabel);
105 strcat(decodedDomainString, nextLabel);
106 strcat(decodedDomainString, ".");
109 // Remove trailing dot from domain name.
110 decodedDomainString[strlen(decodedDomainString)-1] = '\0';
112 domainString = [[[NSString alloc] initWithUTF8String:(const char *)decodedDomainString] autorelease];
114 if (enumType & kDNSServiceFlagsRegistrationDomains) {
115 domainArray = [me registrationDataSource];
116 domainComboBox = [me regDomainsComboBox];
118 domainArray = [me browseDataSource];
119 domainComboBox = [me browseDomainsComboBox];
120 defaultBrowseDomainsArray = [me defaultBrowseDomainsArray];
123 if (flags & kDNSServiceFlagsAdd) {
124 [domainArray removeObject:domainString]; // How can I check if an object is in the array?
125 [domainArray addObject:domainString];
126 if ((flags & kDNSServiceFlagsDefault) && (enumType & kDNSServiceFlagsRegistrationDomains)) {
127 [me setDefaultRegDomain:domainString];
128 if ([[domainComboBox stringValue] length] == 0) [domainComboBox setStringValue:domainString];
129 } else if ((flags & kDNSServiceFlagsDefault) && !(enumType & kDNSServiceFlagsRegistrationDomains)) {
130 [defaultBrowseDomainsArray removeObject:domainString];
131 [defaultBrowseDomainsArray addObject:domainString];
135 if (moreComing == NO) {
136 [domainArray sortUsingFunction:MyArrayCompareFunction context:nil];
137 [domainComboBox reloadData];
143 browseDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
144 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
146 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsBrowseDomains);
151 registrationDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
152 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
154 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsRegistrationDomains);
160 MyDNSServiceCleanUp(MyDNSServiceState * query)
162 /* Remove the CFRunLoopSource from the current run loop. */
163 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
164 CFRelease(query->source);
166 /* Invalidate the CFSocket. */
167 CFSocketInvalidate(query->socket);
168 CFRelease(query->socket);
170 /* Workaround that gives time to CFSocket's select thread so it can remove the socket from its FD set
171 before we close the socket by calling DNSServiceRefDeallocate. <rdar://problem/3585273> */
174 /* Terminate the connection with the mDNSResponder daemon, which cancels the query. */
175 DNSServiceRefDeallocate(query->service);
181 MySocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void * data, void * info)
185 #pragma unused(address)
188 DNSServiceErrorType err;
190 MyDNSServiceState * query = (MyDNSServiceState *)info; // context passed in to CFSocketCreateWithNative().
191 assert(query != NULL);
193 /* Read a reply from the mDNSResponder. */
194 err= DNSServiceProcessResult(query->service);
195 if (err != kDNSServiceErr_NoError) {
196 fprintf(stderr, "DNSServiceProcessResult returned %d\n", err);
198 /* Terminate the query operation and release the CFRunLoopSource and CFSocket. */
199 MyDNSServiceCleanUp(query);
206 MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query)
208 CFSocketNativeHandle sock;
209 CFOptionFlags sockFlags;
210 CFSocketContext context = { 0, query, NULL, NULL, NULL }; // Use MyDNSServiceState as context data.
212 /* Access the underlying Unix domain socket to communicate with the mDNSResponder daemon. */
213 sock = DNSServiceRefSockFD(query->service);
216 /* Create a CFSocket using the Unix domain socket. */
217 query->socket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, MySocketReadCallback, &context);
218 assert(query->socket != NULL);
220 /* Prevent CFSocketInvalidate from closing DNSServiceRef's socket. */
221 sockFlags = CFSocketGetSocketFlags(query->socket);
222 CFSocketSetSocketFlags(query->socket, sockFlags & (~kCFSocketCloseOnInvalidate));
224 /* Create a CFRunLoopSource from the CFSocket. */
225 query->source = CFSocketCreateRunLoopSource(NULL, query->socket, 0);
226 assert(query->source != NULL);
228 /* Add the CFRunLoopSource to the current run loop. */
229 CFRunLoopAddSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
234 -(void)updateStatusImageView
236 int value = [self statusForHostName:currentHostName];
237 if (value == 0) [statusImageView setImage:successImage];
238 else if (value > 0) [statusImageView setImage:inprogressImage];
239 else [statusImageView setImage:failureImage];
243 - (void)watchForPreferenceChanges
245 SCDynamicStoreContext context = { 0, self, NULL, NULL, NULL };
246 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("watchForPreferenceChanges"), NetworkChanged, &context);
247 CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
248 CFRunLoopSourceRef rls;
250 assert(store != NULL);
251 assert(keys != NULL);
253 CFArrayAppendValue(keys, SC_DYNDNS_STATE_KEY);
254 CFArrayAppendValue(keys, SC_DYNDNS_SETUP_KEY);
256 (void)SCDynamicStoreSetNotificationKeys(store, keys, NULL);
258 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
261 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
269 -(int)statusForHostName:(NSString * )domain
271 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("statusForHostName"), NULL, NULL);
272 NSString *lowercaseDomain = [domain lowercaseString];
275 assert(store != NULL);
277 NSDictionary *dynamicDNS = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY);
279 NSDictionary *hostNames = [dynamicDNS objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
280 NSDictionary *infoDict = [hostNames objectForKey:lowercaseDomain];
281 if (infoDict) status = [[infoDict objectForKey:(NSString*)SC_DYNDNS_STATUS_KEY] intValue];
282 CFRelease(dynamicDNS);
290 - (void)startDomainBrowsing
292 DNSServiceFlags flags;
293 OSStatus err = noErr;
295 flags = kDNSServiceFlagsRegistrationDomains;
296 err = DNSServiceEnumerateDomains(®Query.service, flags, 0, registrationDomainReply, (void *)self);
297 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(®Query);
299 flags = kDNSServiceFlagsBrowseDomains;
300 err = DNSServiceEnumerateDomains(&browseQuery.service, flags, 0, browseDomainReply, (void *)self);
301 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(&browseQuery);
305 -(void)readPreferences
307 NSDictionary *origDict;
308 NSArray *regDomainArray;
311 if (currentRegDomain) [currentRegDomain release];
312 if (currentBrowseDomainsArray) [currentBrowseDomainsArray release];
313 if (currentHostName) [currentHostName release];
315 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL);
316 origDict = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY);
318 regDomainArray = [origDict objectForKey:(NSString *)SC_DYNDNS_REGDOMAINS_KEY];
319 if (regDomainArray && [regDomainArray count] > 0) {
320 currentRegDomain = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
321 currentWideAreaState = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
323 currentRegDomain = [[NSString alloc] initWithString:@""];
324 currentWideAreaState = NO;
327 currentBrowseDomainsArray = [[origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY] retain];
329 hostArray = [origDict objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
330 if (hostArray && [hostArray count] > 0) {
331 currentHostName = [[[hostArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
333 currentHostName = [[NSString alloc] initWithString:@""];
336 if (origDict) CFRelease((CFDictionaryRef)origDict);
337 if (store) CFRelease(store);
341 - (void)tableViewSelectionDidChange:(NSNotification *)notification
343 [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]];
347 - (void)setBrowseDomainsComboBox
349 NSString * domain = nil;
351 if ([defaultBrowseDomainsArray count] > 0) {
352 NSEnumerator * arrayEnumerator = [defaultBrowseDomainsArray objectEnumerator];
353 while ((domain = [arrayEnumerator nextObject]) != NULL) {
354 if ([self domainAlreadyInList:domain] == NO) break;
357 if (domain) [browseDomainsComboBox setStringValue:domain];
358 else [browseDomainsComboBox setStringValue:@""];
362 - (IBAction)addBrowseDomainClicked:(id)sender
364 [self setBrowseDomainsComboBox];
366 [NSApp beginSheet:addBrowseDomainWindow modalForWindow:mainWindow modalDelegate:self
367 didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
369 [browseDomainList deselectAll:sender];
370 [self updateApplyButtonState];
374 - (IBAction)removeBrowseDomainClicked:(id)sender
376 (void)sender; // Unused
377 int selectedBrowseDomain = [browseDomainList selectedRow];
378 [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain];
379 [browseDomainList reloadData];
380 [self updateApplyButtonState];
384 - (IBAction)enableBrowseDomainClicked:(id)sender
386 NSTableView *tableView = sender;
387 NSMutableDictionary *browseDomainDict;
390 browseDomainDict = [[browseDomainsArray objectAtIndex:[tableView clickedRow]] mutableCopy];
391 value = [[browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
392 [browseDomainDict setObject:[[[NSNumber alloc] initWithInt:(!value)] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
393 [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict];
394 [tableView reloadData];
395 [self updateApplyButtonState];
396 [browseDomainDict release];
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];
684 if ((currentHostName && ([hostNameString compare:currentHostName] != NSOrderedSame)) ||
685 (currentRegDomain && ([regDomainString compare:currentRegDomain] != NSOrderedSame) && ([wideAreaCheckBox state])) ||
686 (currentHostName == nil && ([hostNameString length]) > 0) ||
687 (currentRegDomain == nil && ([regDomainString length]) > 0) ||
688 (currentWideAreaState != [wideAreaCheckBox state]) ||
689 (hostNameSharedSecretValue != nil) ||
690 (regSharedSecretValue != nil) ||
691 (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO))
693 [self enableApplyButton];
695 [self disableApplyButton];
701 - (void)controlTextDidChange:(NSNotification *)notification
703 (void)notification; // Unused
704 [self updateApplyButtonState];
709 - (IBAction)comboAction:(id)sender
711 (void)sender; // Unused
712 [self updateApplyButtonState];
716 - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)ind
718 NSString *domain = nil;
719 if ([aComboBox isEqualTo:browseDomainsComboBox]) domain = [browseDataSource objectAtIndex:ind];
720 else if ([aComboBox isEqualTo:regDomainsComboBox]) domain = [registrationDataSource objectAtIndex:ind];
726 - (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox
729 if ([aComboBox isEqualTo:browseDomainsComboBox]) count = [browseDataSource count];
730 else if ([aComboBox isEqualTo:regDomainsComboBox]) count = [registrationDataSource count];
735 - (NSMutableArray *)browseDataSource
737 return browseDataSource;
741 - (NSMutableArray *)registrationDataSource
743 return registrationDataSource;
747 - (NSComboBox *)browseDomainsComboBox
749 return browseDomainsComboBox;
753 - (NSComboBox *)regDomainsComboBox
755 return regDomainsComboBox;
759 - (NSString *)currentRegDomain
761 return currentRegDomain;
765 - (NSMutableArray *)defaultBrowseDomainsArray
767 return defaultBrowseDomainsArray;
771 - (NSArray *)currentBrowseDomainsArray
773 return currentBrowseDomainsArray;
777 - (NSString *)currentHostName
779 return currentHostName;
783 - (NSString *)defaultRegDomain
785 return defaultRegDomain;
789 - (void)setDefaultRegDomain:(NSString *)domain
791 [defaultRegDomain release];
792 defaultRegDomain = domain;
793 [defaultRegDomain retain];
800 mainWindow = [[self mainView] window];
804 - (void)mainViewDidLoad
806 [comboAuthButton setString:"system.preferences"];
807 [comboAuthButton setDelegate:self];
808 [comboAuthButton updateStatus:nil];
809 [comboAuthButton setAutoupdate:YES];
814 - (IBAction)applyClicked:(id)sender
816 (void)sender; // Unused
817 [self applyCurrentState];
821 - (void)applyCurrentState
823 [self validateTextFields];
825 if (toolInstalled == YES) {
826 [self savePreferences];
827 [self disableApplyButton];
828 [mainWindow makeFirstResponder:nil];
833 - (void)enableApplyButton
835 [applyButton setEnabled:YES];
836 [revertButton setEnabled:YES];
837 prefsNeedUpdating = YES;
841 - (void)disableApplyButton
843 [applyButton setEnabled:NO];
844 [revertButton setEnabled:NO];
845 prefsNeedUpdating = NO;
849 - (void)toggleWideAreaBonjour:(BOOL)state
851 [wideAreaCheckBox setState:state];
852 [regDomainsComboBox setEnabled:state];
853 [registrationSharedSecretButton setEnabled:state];
857 - (IBAction)revertClicked:(id)sender
859 [self restorePreferences];
860 [browseDomainList deselectAll:sender];
861 [mainWindow makeFirstResponder:nil];
865 - (void)restorePreferences
867 [self setupInitialValues];
871 - (void)savePanelWillClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
873 (void)sheet; // Unused
874 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)contextInfo;
876 if (returnCode == NSAlertDefaultReturn) {
877 [me applyCurrentState];
878 } else if (returnCode == NSAlertAlternateReturn ) {
879 [me restorePreferences];
883 [me replyToShouldUnselect:(returnCode != NSAlertOtherReturn)];
887 -(SecKeychainItemRef)copyKeychainItemforDomain:(NSString *)domain
889 const char * serviceName = [domain UTF8String];
890 UInt32 type = 'ddns';
891 UInt32 typeLength = sizeof(type);
893 SecKeychainAttribute attrs[] = { { kSecServiceItemAttr, strlen(serviceName), (char *)serviceName },
894 { kSecTypeItemAttr, typeLength, (UInt32 *)&type } };
896 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
897 SecKeychainSearchRef searchRef;
898 SecKeychainItemRef itemRef = NULL;
901 err = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef);
903 err = SecKeychainSearchCopyNext(searchRef, &itemRef);
904 if (err != noErr) itemRef = NULL;
910 -(NSString *)sharedSecretKeyName:(NSString * )domain
912 SecKeychainItemRef itemRef = NULL;
913 NSString *keyName = nil;
916 err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
917 assert(err == noErr);
919 itemRef = [self copyKeychainItemforDomain:[domain lowercaseString]];
922 SecKeychainAttributeInfo attrInfo;
923 SecKeychainAttributeList *attrList = NULL;
924 SecKeychainAttribute attribute;
927 tags[0] = kSecAccountItemAttr;
930 attrInfo.format = NULL;
932 err = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, NULL, NULL);
934 for (i = 0; i < attrList->count; i++) {
935 attribute = attrList->attr[i];
936 if (attribute.tag == kSecAccountItemAttr) {
937 keyName = [[NSString alloc] initWithBytes:attribute.data length:attribute.length encoding:NSUTF8StringEncoding];
941 if (attrList) (void)SecKeychainItemFreeAttributesAndData(attrList, NULL);
945 return [keyName autorelease];
949 -(NSString *)domainForHostName:(NSString *)hostNameString
951 NSString * domainName = nil;
955 ptr = (char *)[hostNameString UTF8String];
957 ptr = (char *)GetNextLabel(ptr, text);
958 domainName = [[NSString alloc] initWithUTF8String:(const char *)ptr];
960 return ([domainName autorelease]);
964 - (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled
966 NSMutableArray *domainsArray;
967 NSMutableDictionary *domainDict = nil;
969 if (domainName && [domainName length] > 0) {
970 domainDict= [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
971 [domainDict setObject:domainName forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
972 [domainDict setObject:[[[NSNumber alloc] initWithBool:enabled] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
974 domainsArray = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
975 if (domainDict) [domainsArray addObject:domainDict];
976 return [NSArchiver archivedDataWithRootObject:domainsArray];
980 - (NSData *)dataForDomainArray:(NSArray *)domainArray
982 return [NSArchiver archivedDataWithRootObject:domainArray];
986 - (NSData *)dataForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName
988 NSMutableDictionary *sharedSecretDict = [[[NSMutableDictionary alloc] initWithCapacity:3] autorelease];
989 [sharedSecretDict setObject:secret forKey:(NSString *)SC_DYNDNS_SECRET_KEY];
990 [sharedSecretDict setObject:[domainName lowercaseString] forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
991 [sharedSecretDict setObject:keyName forKey:(NSString *)SC_DYNDNS_KEYNAME_KEY];
992 return [NSArchiver archivedDataWithRootObject:sharedSecretDict];
996 -(void)savePreferences
998 NSString *hostNameString = [hostName stringValue];
999 NSString *regDomainString = [regDomainsComboBox stringValue];
1000 NSString *tempHostNameSharedSecretName = hostNameSharedSecretName;
1001 NSString *tempRegSharedSecretName = regSharedSecretName;
1002 NSData *browseDomainData = nil;
1003 BOOL regSecretWasSet = NO;
1004 BOOL hostSecretWasSet = NO;
1005 OSStatus err = noErr;
1007 hostNameString = [self trimCharactersFromDomain:hostNameString];
1008 regDomainString = [self trimCharactersFromDomain:regDomainString];
1009 tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName];
1010 tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName];
1012 [hostName setStringValue:hostNameString];
1013 [regDomainsComboBox setStringValue:regDomainString];
1015 // Convert Shared Secret account names to lowercase.
1016 tempHostNameSharedSecretName = [tempHostNameSharedSecretName lowercaseString];
1017 tempRegSharedSecretName = [tempRegSharedSecretName lowercaseString];
1019 // Save hostname shared secret.
1020 if ([hostNameSharedSecretName length] > 0 && ([hostNameSharedSecretValue length] > 0)) {
1021 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:hostNameSharedSecretValue domain:hostNameString key:tempHostNameSharedSecretName]);
1022 [hostNameSharedSecretValue release];
1023 hostNameSharedSecretValue = nil;
1024 hostSecretWasSet = YES;
1027 // Save registration domain shared secret.
1028 if (([regSharedSecretName length] > 0) && ([regSharedSecretValue length] > 0)) {
1029 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:regSharedSecretValue domain:regDomainString key:tempRegSharedSecretName]);
1030 [regSharedSecretValue release];
1031 regSharedSecretValue = nil;
1032 regSecretWasSet = YES;
1036 if ((currentHostName == NULL) || [currentHostName compare:hostNameString] != NSOrderedSame) {
1037 err = WriteHostname((CFDataRef)[self dataForDomain:hostNameString isEnabled:YES]);
1038 if (err != noErr) NSLog(@"WriteHostname returned %d\n", (int32_t)err);
1039 currentHostName = [hostNameString copy];
1040 } else if (hostSecretWasSet) {
1041 WriteHostname((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1042 usleep(200000); // Temporary hack
1043 if ([currentHostName length] > 0) WriteHostname((CFDataRef)[self dataForDomain:(NSString *)currentHostName isEnabled:YES]);
1046 // Save browse domain.
1047 if (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
1048 browseDomainData = [self dataForDomainArray:browseDomainsArray];
1049 err = WriteBrowseDomain((CFDataRef)browseDomainData);
1050 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", (int32_t)err);
1051 currentBrowseDomainsArray = [browseDomainsArray copy];
1054 // Save registration domain.
1055 if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) {
1057 err = WriteRegistrationDomain((CFDataRef)[self dataForDomain:regDomainString isEnabled:[wideAreaCheckBox state]]);
1058 if (err != noErr) NSLog(@"WriteRegistrationDomain returned %d\n", (int32_t)err);
1060 if (currentRegDomain) CFRelease(currentRegDomain);
1061 currentRegDomain = [regDomainString copy];
1063 if ([currentRegDomain length] > 0) {
1064 currentWideAreaState = [wideAreaCheckBox state];
1065 [registrationDataSource removeObject:regDomainString];
1066 [registrationDataSource addObject:currentRegDomain];
1067 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
1068 [regDomainsComboBox reloadData];
1070 currentWideAreaState = NO;
1071 [self toggleWideAreaBonjour:NO];
1072 if (defaultRegDomain != nil) [regDomainsComboBox setStringValue:defaultRegDomain];
1074 } else if (regSecretWasSet) {
1075 WriteRegistrationDomain((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1076 usleep(200000); // Temporary hack
1077 if ([currentRegDomain length] > 0) WriteRegistrationDomain((CFDataRef)[self dataForDomain:currentRegDomain isEnabled:currentWideAreaState]);
1082 - (NSPreferencePaneUnselectReply)shouldUnselect
1085 if (prefsNeedUpdating == YES) {
1087 [self disableControls];
1090 @"Apply Configuration Changes?",
1096 @selector( savePanelWillClose:returnCode:contextInfo: ),
1098 (void *) self, // sender,
1100 return NSUnselectLater;
1104 return NSUnselectNow;
1108 -(void)disableControls
1110 [hostName setEnabled:NO];
1111 [hostNameSharedSecretButton setEnabled:NO];
1112 [browseDomainsComboBox setEnabled:NO];
1113 [applyButton setEnabled:NO];
1114 [revertButton setEnabled:NO];
1115 [wideAreaCheckBox setEnabled:NO];
1116 [regDomainsComboBox setEnabled:NO];
1117 [registrationSharedSecretButton setEnabled:NO];
1118 [statusImageView setEnabled:NO];
1120 browseDomainListEnabled = NO;
1121 [browseDomainList deselectAll:self];
1122 [browseDomainList setEnabled:NO];
1124 [addBrowseDomainButton setEnabled:NO];
1125 [removeBrowseDomainButton setEnabled:NO];
1129 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row
1131 (void)row; // Unused
1132 (void)tableView; // Unused
1133 return browseDomainListEnabled;
1137 -(void)enableControls
1139 [hostName setEnabled:YES];
1140 [hostNameSharedSecretButton setEnabled:YES];
1141 [browseDomainsComboBox setEnabled:YES];
1142 [wideAreaCheckBox setEnabled:YES];
1143 [registrationSharedSecretButton setEnabled:YES];
1144 [self toggleWideAreaBonjour:[wideAreaCheckBox state]];
1145 [statusImageView setEnabled:YES];
1146 [addBrowseDomainButton setEnabled:YES];
1148 [browseDomainList setEnabled:YES];
1149 [browseDomainList deselectAll:self];
1150 browseDomainListEnabled = YES;
1152 [removeBrowseDomainButton setEnabled:[browseDomainList numberOfSelectedRows]];
1153 [applyButton setEnabled:prefsNeedUpdating];
1154 [revertButton setEnabled:prefsNeedUpdating];
1158 - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view
1160 (void)view; // Unused
1161 [self enableControls];
1165 - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view
1167 (void)view; // Unused
1168 [self disableControls];
1174 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
1175 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
1176 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
1177 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
1178 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
1180 // NOT static -- otherwise the compiler may optimize it out
1181 // The "@(#) " pattern is a special prefix the "what" command looks for
1182 const char VersionString_SCCS[] = "@(#) Bonjour Preference Pane " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
1184 #if _BUILDING_XCODE_PROJECT_
1185 // If the process crashes, then this string will be magically included in the automatically-generated crash log
1186 const char *__crashreporter_info__ = VersionString_SCCS + 5;
1187 asm(".desc ___crashreporter_info__, 0x10");