2 File: DNSServiceDiscoveryPref.m
4 Abstract: System Preference Pane for Dynamic DNS and Wide-Area DNS Service Discovery
6 Copyright: (c) Copyright 2005 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.
43 Change History (most recent first):
45 $Log: DNSServiceDiscoveryPref.m,v $
46 Revision 1.10 2007/09/18 19:09:02 cheshire
47 <rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings
49 Revision 1.9 2007/02/09 00:39:06 cheshire
52 Revision 1.8 2006/08/14 23:15:47 cheshire
53 Tidy up Change History comment
55 Revision 1.7 2006/07/14 03:59:14 cheshire
56 Fix compile warnings: 'sortUsingFunction:context:' comparison function needs to return int
58 Revision 1.6 2005/02/26 00:44:24 cheshire
59 Restore default reg domain if user deletes text and clicks "apply"
61 Revision 1.5 2005/02/25 02:29:28 cheshire
62 Show yellow dot for "update in progress"
64 Revision 1.4 2005/02/16 00:18:33 cheshire
67 Revision 1.3 2005/02/10 22:35:20 cheshire
68 <rdar://problem/3727944> Update name
70 Revision 1.2 2005/02/08 01:32:05 cheshire
71 Add trimCharactersFromDomain routine to strip leading and trailing
72 white space and punctuation from user-entered fields.
74 Revision 1.1 2005/02/05 01:59:19 cheshire
75 Add Preference Pane to facilitate testing of DDNS & wide-area features
79 #import "DNSServiceDiscoveryPref.h"
80 #import "ConfigurationAuthority.h"
81 #import "PrivilegedOperations.h"
84 @implementation DNSServiceDiscoveryPref
87 MyArrayCompareFunction(id val1, id val2, void *context)
89 (void)context; // Unused
90 return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive);
95 MyDomainArrayCompareFunction(id val1, id val2, void *context)
97 (void)context; // Unused
98 NSString *domain1 = [val1 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
99 NSString *domain2 = [val2 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
100 return CFStringCompare((CFStringRef)domain1, (CFStringRef)domain2, kCFCompareCaseInsensitive);
105 GetNextLabel(const char *cstr, char label[64])
108 while (*cstr && *cstr != '.') // While we have characters in the label...
114 if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1]))
116 int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal
117 int v1 = cstr[ 0] - '0';
118 int v2 = cstr[ 1] - '0';
119 int val = v0 * 100 + v1 * 10 + v2;
120 if (val <= 255) { c = (char)val; cstr += 2; } // If valid three-digit decimal value, use it
124 if (ptr >= label+64) return(NULL);
126 if (*cstr) cstr++; // Skip over the trailing dot (if present)
132 static void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
134 (void)store; // Unused
135 (void)changedKeys; // Unused
136 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
139 [me setupInitialValues];
143 static void ServiceDomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
144 DNSServiceErrorType errorCode, const char *replyDomain, void *context, DNSServiceFlags enumType)
146 (void)sdRef; // Unused
147 (void)interfaceIndex; // Unused
148 (void)errorCode; // Unused
149 if (strcmp(replyDomain, "local.") == 0) return; // local domain is not interesting
151 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
152 BOOL moreComing = (BOOL)(flags & kDNSServiceFlagsMoreComing);
153 NSMutableArray * domainArray;
154 NSMutableArray * defaultBrowseDomainsArray = nil;
155 NSComboBox * domainComboBox;
156 NSString * domainString;
157 NSString * currentDomain = nil;
158 char decodedDomainString[kDNSServiceMaxDomainName] = "\0";
159 char nextLabel[256] = "\0";
160 char * buffer = (char *)replyDomain;
163 buffer = (char *)GetNextLabel(buffer, nextLabel);
164 strcat(decodedDomainString, nextLabel);
165 strcat(decodedDomainString, ".");
168 // Remove trailing dot from domain name.
169 decodedDomainString[strlen(decodedDomainString)-1] = '\0';
171 domainString = [[[NSString alloc] initWithUTF8String:(const char *)decodedDomainString] autorelease];
173 if (enumType & kDNSServiceFlagsRegistrationDomains) {
174 domainArray = [me registrationDataSource];
175 domainComboBox = [me regDomainsComboBox];
176 currentDomain = [me currentRegDomain];
178 domainArray = [me browseDataSource];
179 domainComboBox = [me browseDomainsComboBox];
180 defaultBrowseDomainsArray = [me defaultBrowseDomainsArray];
183 if (flags & kDNSServiceFlagsAdd) {
184 [domainArray removeObject:domainString]; // How can I check if an object is in the array?
185 [domainArray addObject:domainString];
186 if ((flags & kDNSServiceFlagsDefault) && (enumType & kDNSServiceFlagsRegistrationDomains)) {
187 [me setDefaultRegDomain:domainString];
188 if ([[domainComboBox stringValue] length] == 0) [domainComboBox setStringValue:domainString];
189 } else if ((flags & kDNSServiceFlagsDefault) && !(enumType & kDNSServiceFlagsRegistrationDomains)) {
190 [defaultBrowseDomainsArray removeObject:domainString];
191 [defaultBrowseDomainsArray addObject:domainString];
195 if (moreComing == NO) {
196 [domainArray sortUsingFunction:MyArrayCompareFunction context:nil];
197 [domainComboBox reloadData];
203 browseDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
204 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
206 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsBrowseDomains);
211 registrationDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
212 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
214 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsRegistrationDomains);
220 MyDNSServiceCleanUp(MyDNSServiceState * query)
222 /* Remove the CFRunLoopSource from the current run loop. */
223 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
224 CFRelease(query->source);
226 /* Invalidate the CFSocket. */
227 CFSocketInvalidate(query->socket);
228 CFRelease(query->socket);
230 /* Workaround that gives time to CFSocket's select thread so it can remove the socket from its FD set
231 before we close the socket by calling DNSServiceRefDeallocate. <rdar://problem/3585273> */
234 /* Terminate the connection with the mDNSResponder daemon, which cancels the query. */
235 DNSServiceRefDeallocate(query->service);
241 MySocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void * data, void * info)
245 #pragma unused(address)
248 DNSServiceErrorType err;
250 MyDNSServiceState * query = (MyDNSServiceState *)info; // context passed in to CFSocketCreateWithNative().
251 assert(query != NULL);
253 /* Read a reply from the mDNSResponder. */
254 err= DNSServiceProcessResult(query->service);
255 if (err != kDNSServiceErr_NoError) {
256 fprintf(stderr, "DNSServiceProcessResult returned %d\n", err);
258 /* Terminate the query operation and release the CFRunLoopSource and CFSocket. */
259 MyDNSServiceCleanUp(query);
266 MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query)
268 CFSocketNativeHandle sock;
269 CFOptionFlags sockFlags;
270 CFSocketContext context = { 0, query, NULL, NULL, NULL }; // Use MyDNSServiceState as context data.
272 /* Access the underlying Unix domain socket to communicate with the mDNSResponder daemon. */
273 sock = DNSServiceRefSockFD(query->service);
276 /* Create a CFSocket using the Unix domain socket. */
277 query->socket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, MySocketReadCallback, &context);
278 assert(query->socket != NULL);
280 /* Prevent CFSocketInvalidate from closing DNSServiceRef's socket. */
281 sockFlags = CFSocketGetSocketFlags(query->socket);
282 CFSocketSetSocketFlags(query->socket, sockFlags & (~kCFSocketCloseOnInvalidate));
284 /* Create a CFRunLoopSource from the CFSocket. */
285 query->source = CFSocketCreateRunLoopSource(NULL, query->socket, 0);
286 assert(query->source != NULL);
288 /* Add the CFRunLoopSource to the current run loop. */
289 CFRunLoopAddSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
294 -(void)updateStatusImageView
296 int value = [self statusForHostName:currentHostName];
297 if (value == 0) [statusImageView setImage:successImage];
298 else if (value > 0) [statusImageView setImage:inprogressImage];
299 else [statusImageView setImage:failureImage];
303 - (void)watchForPreferenceChanges
305 SCDynamicStoreContext context = { 0, self, NULL, NULL, NULL };
306 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("watchForPreferenceChanges"), NetworkChanged, &context);
307 CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
308 CFRunLoopSourceRef rls;
310 assert(store != NULL);
311 assert(keys != NULL);
313 CFArrayAppendValue(keys, SC_DYNDNS_STATE_KEY);
314 CFArrayAppendValue(keys, SC_DYNDNS_SETUP_KEY);
316 (void)SCDynamicStoreSetNotificationKeys(store, keys, NULL);
318 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
321 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
328 -(int)statusForHostName:(NSString * )domain
330 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("statusForHostName"), NULL, NULL);
331 NSString *lowercaseDomain = [domain lowercaseString];
334 assert(store != NULL);
336 NSDictionary *dynamicDNS = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY);
338 NSDictionary *hostNames = [dynamicDNS objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
339 NSDictionary *infoDict = [hostNames objectForKey:lowercaseDomain];
340 if (infoDict) status = [[infoDict objectForKey:(NSString*)SC_DYNDNS_STATUS_KEY] intValue];
341 CFRelease(dynamicDNS);
349 - (void)startDomainBrowsing
351 DNSServiceFlags flags;
352 OSStatus err = noErr;
354 flags = kDNSServiceFlagsRegistrationDomains;
355 err = DNSServiceEnumerateDomains(®Query.service, flags, 0, registrationDomainReply, (void *)self);
356 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(®Query);
358 flags = kDNSServiceFlagsBrowseDomains;
359 err = DNSServiceEnumerateDomains(&browseQuery.service, flags, 0, browseDomainReply, (void *)self);
360 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(&browseQuery);
364 -(void)readPreferences
366 NSDictionary *origDict;
367 NSArray *regDomainArray;
370 if (currentRegDomain) [currentRegDomain release];
371 if (currentBrowseDomainsArray) [currentBrowseDomainsArray release];
372 if (currentHostName) [currentHostName release];
374 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL);
375 origDict = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY);
377 regDomainArray = [origDict objectForKey:(NSString *)SC_DYNDNS_REGDOMAINS_KEY];
378 if (regDomainArray && [regDomainArray count] > 0) {
379 currentRegDomain = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
380 currentWideAreaState = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
382 currentRegDomain = [[NSString alloc] initWithString:@""];
383 currentWideAreaState = NO;
386 currentBrowseDomainsArray = [[origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY] retain];
388 hostArray = [origDict objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
389 if (hostArray && [hostArray count] > 0) {
390 currentHostName = [[[hostArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
392 currentHostName = [[NSString alloc] initWithString:@""];
400 - (void)tableViewSelectionDidChange:(NSNotification *)notification;
402 [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]];
406 - (void)setBrowseDomainsComboBox;
408 NSString * domain = nil;
410 if ([defaultBrowseDomainsArray count] > 0) {
411 NSEnumerator * arrayEnumerator = [defaultBrowseDomainsArray objectEnumerator];
412 while ((domain = [arrayEnumerator nextObject]) != NULL) {
413 if ([self domainAlreadyInList:domain] == NO) break;
416 if (domain) [browseDomainsComboBox setStringValue:domain];
417 else [browseDomainsComboBox setStringValue:@""];
421 - (IBAction)addBrowseDomainClicked:(id)sender;
423 [self setBrowseDomainsComboBox];
425 [NSApp beginSheet:addBrowseDomainWindow modalForWindow:mainWindow modalDelegate:self
426 didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
428 [browseDomainList deselectAll:sender];
429 [self updateApplyButtonState];
433 - (IBAction)removeBrowseDomainClicked:(id)sender;
435 (void)sender; // Unused
436 int selectedBrowseDomain = [browseDomainList selectedRow];
437 [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain];
438 [browseDomainList reloadData];
439 [self updateApplyButtonState];
443 - (IBAction)enableBrowseDomainClicked:(id)sender;
445 NSTableView *tableView = sender;
446 NSMutableDictionary *browseDomainDict;
449 browseDomainDict = [[browseDomainsArray objectAtIndex:[tableView clickedRow]] mutableCopy];
450 value = [[browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
451 [browseDomainDict setObject:[[[NSNumber alloc] initWithInt:(!value)] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
452 [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict];
453 [tableView reloadData];
454 [self updateApplyButtonState];
459 - (int)numberOfRowsInTableView:(NSTableView *)tableView;
461 (void)tableView; // Unused
462 int numberOfRows = 0;
464 if (browseDomainsArray) {
465 numberOfRows = [browseDomainsArray count];
471 - (void)tabView:(NSTabView *)xtabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem;
473 (void)xtabView; // Unused
474 (void)tabViewItem; // Unused
475 [browseDomainList deselectAll:self];
476 [mainWindow makeFirstResponder:nil];
480 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row;
482 (void)tableView; // Unused
483 NSDictionary *browseDomainDict;
486 if (browseDomainsArray) {
487 browseDomainDict = [browseDomainsArray objectAtIndex:row];
488 if (browseDomainDict) {
489 if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_ENABLED_KEY]) {
490 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
491 } else if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_DOMAIN_KEY]) {
492 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
500 - (void)setupInitialValues
502 [self readPreferences];
504 if (currentHostName) {
505 [hostName setStringValue:currentHostName];
506 [self updateStatusImageView];
509 if (browseDomainsArray) {
510 [browseDomainsArray release];
511 browseDomainsArray = nil;
514 if (currentBrowseDomainsArray) {
515 browseDomainsArray = [currentBrowseDomainsArray mutableCopy];
516 if (browseDomainsArray) {
517 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
518 if ([browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
519 OSStatus err = WriteBrowseDomain((CFDataRef)[self dataForDomainArray:browseDomainsArray]);
520 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", err);
521 [currentBrowseDomainsArray release];
522 currentBrowseDomainsArray = [browseDomainsArray copy];
526 browseDomainsArray = nil;
528 [browseDomainList reloadData];
530 if (currentRegDomain && ([currentRegDomain length] > 0)) {
531 [regDomainsComboBox setStringValue:currentRegDomain];
532 [registrationDataSource removeObject:currentRegDomain];
533 [registrationDataSource addObject:currentRegDomain];
534 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
535 [regDomainsComboBox reloadData];
538 if (currentWideAreaState) {
539 [self toggleWideAreaBonjour:YES];
541 [self toggleWideAreaBonjour:NO];
544 if (hostNameSharedSecretValue) {
545 [hostNameSharedSecretValue release];
546 hostNameSharedSecretValue = nil;
549 if (regSharedSecretValue) {
550 [regSharedSecretValue release];
551 regSharedSecretValue = nil;
554 [self updateApplyButtonState];
555 [mainWindow makeFirstResponder:nil];
556 [browseDomainList deselectAll:self];
557 [removeBrowseDomainButton setEnabled:NO];
566 prefsNeedUpdating = NO;
568 browseDomainListEnabled = NO;
569 defaultRegDomain = nil;
570 currentRegDomain = nil;
571 currentBrowseDomainsArray = nil;
572 currentHostName = nil;
573 hostNameSharedSecretValue = nil;
574 regSharedSecretValue = nil;
575 browseDomainsArray = nil;
576 justStartedEditing = YES;
577 currentWideAreaState = NO;
578 NSString *successPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"success" ofType:@"tiff"];
579 NSString *inprogressPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"inprogress" ofType:@"tiff"];
580 NSString *failurePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"failure" ofType:@"tiff"];
582 registrationDataSource = [[NSMutableArray alloc] init];
583 browseDataSource = [[NSMutableArray alloc] init];
584 defaultBrowseDomainsArray = [[NSMutableArray alloc] init];
585 successImage = [[NSImage alloc] initWithContentsOfFile:successPath];
586 inprogressImage = [[NSImage alloc] initWithContentsOfFile:inprogressPath];
587 failureImage = [[NSImage alloc] initWithContentsOfFile:failurePath];
589 [tabView selectFirstTabViewItem:self];
590 [self setupInitialValues];
591 [self startDomainBrowsing];
592 [self watchForPreferenceChanges];
594 [tabView setDelegate:self];
596 InitConfigAuthority();
597 err = EnsureToolInstalled();
598 if (err == noErr) toolInstalled = YES;
599 else fprintf(stderr, "EnsureToolInstalled returned %ld\n", err);
604 - (IBAction)closeMyCustomSheet:(id)sender
606 BOOL result = [sender isEqualTo:browseOKButton] || [sender isEqualTo:secretOKButton];
608 if (result) [NSApp endSheet:[sender window] returnCode:NSOKButton];
609 else [NSApp endSheet:[sender window] returnCode:NSCancelButton];
613 - (void)sharedSecretSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
615 NSButton * button = (NSButton *)contextInfo;
616 [sheet orderOut:self];
617 [self enableControls];
619 if (returnCode == NSOKButton) {
620 if ([button isEqualTo:hostNameSharedSecretButton]) {
621 hostNameSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
622 hostNameSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
624 regSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
625 regSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
627 [self updateApplyButtonState];
629 [sharedSecretValue setStringValue:@""];
633 - (BOOL)domainAlreadyInList:(NSString *)domainString
635 if (browseDomainsArray) {
636 NSDictionary *domainDict;
637 NSString *domainName;
638 NSEnumerator *arrayEnumerator = [browseDomainsArray objectEnumerator];
639 while ((domainDict = [arrayEnumerator nextObject]) != NULL) {
640 domainName = [domainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
641 if ([domainString caseInsensitiveCompare:domainName] == NSOrderedSame) return YES;
648 - (NSString *)trimCharactersFromDomain:(NSString *)domain
650 NSMutableCharacterSet * trimSet = [[[NSCharacterSet whitespaceCharacterSet] mutableCopy] autorelease];
651 [trimSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
652 return [domain stringByTrimmingCharactersInSet:trimSet];
656 - (void)addBrowseDomainSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
658 (void)contextInfo; // Unused
659 [sheet orderOut:self];
660 [self enableControls];
662 if (returnCode == NSOKButton) {
663 NSString * newBrowseDomainString = [self trimCharactersFromDomain:[browseDomainsComboBox stringValue]];
664 NSMutableDictionary *newBrowseDomainDict;
666 if (browseDomainsArray == nil) browseDomainsArray = [[NSMutableArray alloc] initWithCapacity:0];
667 if ([self domainAlreadyInList:newBrowseDomainString] == NO) {
668 newBrowseDomainDict = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
670 [newBrowseDomainDict setObject:newBrowseDomainString forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
671 [newBrowseDomainDict setObject:[[[NSNumber alloc] initWithBool:YES] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
673 [browseDomainsArray addObject:newBrowseDomainDict];
674 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
675 [browseDomainList reloadData];
676 [self updateApplyButtonState];
682 -(void)validateTextFields
684 [hostName validateEditing];
685 [browseDomainsComboBox validateEditing];
686 [regDomainsComboBox validateEditing];
690 - (IBAction)changeButtonPressed:(id)sender
694 [self disableControls];
695 [self validateTextFields];
696 [mainWindow makeFirstResponder:nil];
697 [browseDomainList deselectAll:sender];
699 if ([sender isEqualTo:hostNameSharedSecretButton]) {
700 if (hostNameSharedSecretValue) {
701 [sharedSecretValue setStringValue:hostNameSharedSecretValue];
702 } else if ((keyName = [self sharedSecretKeyName:[hostName stringValue]]) != NULL) {
703 [sharedSecretName setStringValue:keyName];
704 [sharedSecretValue setStringValue:@"****************"];
706 [sharedSecretName setStringValue:[hostName stringValue]];
707 [sharedSecretValue setStringValue:@""];
711 if (regSharedSecretValue) {
712 [sharedSecretValue setStringValue:regSharedSecretValue];
713 } else if ((keyName = [self sharedSecretKeyName:[regDomainsComboBox stringValue]]) != NULL) {
714 [sharedSecretName setStringValue:keyName];
715 [sharedSecretValue setStringValue:@"****************"];
717 [sharedSecretName setStringValue:[regDomainsComboBox stringValue]];
718 [sharedSecretValue setStringValue:@""];
722 [sharedSecretWindow resignFirstResponder];
724 if ([[sharedSecretName stringValue] length] > 0) [sharedSecretWindow makeFirstResponder:sharedSecretValue];
725 else [sharedSecretWindow makeFirstResponder:sharedSecretName];
727 [NSApp beginSheet:sharedSecretWindow modalForWindow:mainWindow modalDelegate:self
728 didEndSelector:@selector(sharedSecretSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
732 - (IBAction)wideAreaCheckBoxChanged:(id)sender
734 [self toggleWideAreaBonjour:[sender state]];
735 [self updateApplyButtonState];
736 [mainWindow makeFirstResponder:nil];
740 - (void)updateApplyButtonState
742 NSString *hostNameString = [hostName stringValue];
743 NSString *regDomainString = [regDomainsComboBox stringValue];
745 NSComparisonResult hostNameResult = [hostNameString compare:currentHostName];
746 NSComparisonResult regDomainResult = [regDomainString compare:currentRegDomain];
748 if ((currentHostName && (hostNameResult != NSOrderedSame)) ||
749 (currentRegDomain && (regDomainResult != NSOrderedSame) && ([wideAreaCheckBox state])) ||
750 (currentHostName == nil && ([hostNameString length]) > 0) ||
751 (currentRegDomain == nil && ([regDomainString length]) > 0) ||
752 (currentWideAreaState != [wideAreaCheckBox state]) ||
753 (hostNameSharedSecretValue != nil) ||
754 (regSharedSecretValue != nil) ||
755 (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO))
757 [self enableApplyButton];
759 [self disableApplyButton];
765 - (void)controlTextDidChange:(NSNotification *)notification;
767 (void)notification; // Unused
768 [self updateApplyButtonState];
773 - (IBAction)comboAction:(id)sender;
775 (void)sender; // Unused
776 [self updateApplyButtonState];
780 - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)index
782 NSString *domain = nil;
783 if ([aComboBox isEqualTo:browseDomainsComboBox]) domain = [browseDataSource objectAtIndex:index];
784 else if ([aComboBox isEqualTo:regDomainsComboBox]) domain = [registrationDataSource objectAtIndex:index];
790 - (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox
793 if ([aComboBox isEqualTo:browseDomainsComboBox]) count = [browseDataSource count];
794 else if ([aComboBox isEqualTo:regDomainsComboBox]) count = [registrationDataSource count];
799 - (NSMutableArray *)browseDataSource
801 return browseDataSource;
805 - (NSMutableArray *)registrationDataSource
807 return registrationDataSource;
811 - (NSComboBox *)browseDomainsComboBox
813 return browseDomainsComboBox;
817 - (NSComboBox *)regDomainsComboBox
819 return regDomainsComboBox;
823 - (NSString *)currentRegDomain
825 return currentRegDomain;
829 - (NSMutableArray *)defaultBrowseDomainsArray
831 return defaultBrowseDomainsArray;
835 - (NSArray *)currentBrowseDomainsArray
837 return currentBrowseDomainsArray;
841 - (NSString *)currentHostName
843 return currentHostName;
847 - (NSString *)defaultRegDomain
849 return defaultRegDomain;
853 - (void)setDefaultRegDomain:(NSString *)domain
855 [defaultRegDomain release];
856 defaultRegDomain = domain;
857 [defaultRegDomain retain];
864 mainWindow = [[self mainView] window];
868 - (void)mainViewDidLoad
870 [comboAuthButton setString:"system.preferences"];
871 [comboAuthButton setDelegate:self];
872 [comboAuthButton updateStatus:nil];
873 [comboAuthButton setAutoupdate:YES];
878 - (IBAction)applyClicked:(id)sender
880 (void)sender; // Unused
881 [self applyCurrentState];
885 - (void)applyCurrentState
887 [self validateTextFields];
889 if (toolInstalled == YES) {
890 [self savePreferences];
891 [self disableApplyButton];
892 [mainWindow makeFirstResponder:nil];
897 - (void)enableApplyButton
899 [applyButton setEnabled:YES];
900 [revertButton setEnabled:YES];
901 prefsNeedUpdating = YES;
905 - (void)disableApplyButton
907 [applyButton setEnabled:NO];
908 [revertButton setEnabled:NO];
909 prefsNeedUpdating = NO;
913 - (void)toggleWideAreaBonjour:(BOOL)state
915 [wideAreaCheckBox setState:state];
916 [regDomainsComboBox setEnabled:state];
917 [registrationSharedSecretButton setEnabled:state];
921 - (IBAction)revertClicked:(id)sender;
923 [self restorePreferences];
924 [browseDomainList deselectAll:sender];
925 [mainWindow makeFirstResponder:nil];
929 - (void)restorePreferences
931 [self setupInitialValues];
935 - (void)savePanelWillClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
937 (void)sheet; // Unused
938 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)contextInfo;
940 if (returnCode == NSAlertDefaultReturn) {
941 [me applyCurrentState];
942 } else if (returnCode == NSAlertAlternateReturn ) {
943 [me restorePreferences];
947 [me replyToShouldUnselect:(returnCode != NSAlertOtherReturn)];
951 -(SecKeychainItemRef)copyKeychainItemforDomain:(NSString *)domain
953 const char * serviceName = [domain UTF8String];
954 UInt32 type = 'ddns';
955 UInt32 typeLength = sizeof(type);
957 SecKeychainAttribute attrs[] = { { kSecServiceItemAttr, strlen(serviceName), (char *)serviceName },
958 { kSecTypeItemAttr, typeLength, (UInt32 *)&type } };
960 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
961 SecKeychainSearchRef searchRef;
962 SecKeychainItemRef itemRef = NULL;
965 err = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef);
967 err = SecKeychainSearchCopyNext(searchRef, &itemRef);
968 if (err != noErr) itemRef = NULL;
974 -(NSString *)sharedSecretKeyName:(NSString * )domain
976 SecKeychainItemRef itemRef = NULL;
977 NSString *keyName = nil;
980 err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
981 assert(err == noErr);
983 itemRef = [self copyKeychainItemforDomain:[domain lowercaseString]];
986 SecKeychainAttributeInfo attrInfo;
987 SecKeychainAttributeList *attrList = NULL;
988 SecKeychainAttribute attribute;
991 tags[0] = kSecAccountItemAttr;
994 attrInfo.format = NULL;
996 err = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, NULL, NULL);
998 for (i = 0; i < attrList->count; i++) {
999 attribute = attrList->attr[i];
1000 if (attribute.tag == kSecAccountItemAttr) {
1001 keyName = [[NSString alloc] initWithBytes:attribute.data length:attribute.length encoding:NSUTF8StringEncoding];
1005 if (attrList) (void)SecKeychainItemFreeAttributesAndData(attrList, NULL);
1013 -(NSString *)domainForHostName:(NSString *)hostNameString
1015 NSString * domainName = nil;
1019 ptr = (char *)[hostNameString UTF8String];
1021 ptr = (char *)GetNextLabel(ptr, text);
1022 domainName = [[NSString alloc] initWithUTF8String:(const char *)ptr];
1024 return ([domainName autorelease]);
1028 - (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled
1030 NSMutableArray *domainsArray;
1031 NSMutableDictionary *domainDict = nil;
1033 if (domainName && [domainName length] > 0) {
1034 domainDict= [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
1035 [domainDict setObject:domainName forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
1036 [domainDict setObject:[[[NSNumber alloc] initWithBool:enabled] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
1038 domainsArray = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
1039 if (domainDict) [domainsArray addObject:domainDict];
1040 return [NSArchiver archivedDataWithRootObject:domainsArray];
1044 - (NSData *)dataForDomainArray:(NSArray *)domainArray
1046 return [NSArchiver archivedDataWithRootObject:domainArray];
1050 - (NSData *)dataForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName
1052 NSMutableDictionary *sharedSecretDict = [[[NSMutableDictionary alloc] initWithCapacity:3] autorelease];
1053 [sharedSecretDict setObject:secret forKey:(NSString *)SC_DYNDNS_SECRET_KEY];
1054 [sharedSecretDict setObject:[domainName lowercaseString] forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
1055 [sharedSecretDict setObject:keyName forKey:(NSString *)SC_DYNDNS_KEYNAME_KEY];
1056 return [NSArchiver archivedDataWithRootObject:sharedSecretDict];
1060 -(void)savePreferences
1062 NSString *hostNameString = [hostName stringValue];
1063 NSString *browseDomainString = [browseDomainsComboBox stringValue];
1064 NSString *regDomainString = [regDomainsComboBox stringValue];
1065 NSString *tempHostNameSharedSecretName = hostNameSharedSecretName;
1066 NSString *tempRegSharedSecretName = regSharedSecretName;
1067 NSData *browseDomainData = nil;
1068 BOOL regSecretWasSet = NO;
1069 BOOL hostSecretWasSet = NO;
1070 OSStatus err = noErr;
1072 hostNameString = [self trimCharactersFromDomain:hostNameString];
1073 browseDomainString = [self trimCharactersFromDomain:browseDomainString];
1074 regDomainString = [self trimCharactersFromDomain:regDomainString];
1075 tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName];
1076 tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName];
1078 [hostName setStringValue:hostNameString];
1079 [regDomainsComboBox setStringValue:regDomainString];
1081 // Convert Shared Secret account names to lowercase.
1082 tempHostNameSharedSecretName = [tempHostNameSharedSecretName lowercaseString];
1083 tempRegSharedSecretName = [tempRegSharedSecretName lowercaseString];
1085 // Save hostname shared secret.
1086 if ([hostNameSharedSecretName length] > 0 && ([hostNameSharedSecretValue length] > 0)) {
1087 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:hostNameSharedSecretValue domain:hostNameString key:tempHostNameSharedSecretName]);
1088 [hostNameSharedSecretValue release];
1089 hostNameSharedSecretValue = nil;
1090 hostSecretWasSet = YES;
1093 // Save registration domain shared secret.
1094 if (([regSharedSecretName length] > 0) && ([regSharedSecretValue length] > 0)) {
1095 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:regSharedSecretValue domain:regDomainString key:tempRegSharedSecretName]);
1096 [regSharedSecretValue release];
1097 regSharedSecretValue = nil;
1098 regSecretWasSet = YES;
1102 if ((currentHostName == NULL) || [currentHostName compare:hostNameString] != NSOrderedSame) {
1103 err = WriteHostname((CFDataRef)[self dataForDomain:hostNameString isEnabled:YES]);
1104 if (err != noErr) NSLog(@"WriteHostname returned %d\n", err);
1105 currentHostName = [hostNameString copy];
1106 } else if (hostSecretWasSet) {
1107 WriteHostname((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1108 usleep(200000); // Temporary hack
1109 if ([currentHostName length] > 0) WriteHostname((CFDataRef)[self dataForDomain:(NSString *)currentHostName isEnabled:YES]);
1112 // Save browse domain.
1113 if (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
1114 browseDomainData = [self dataForDomainArray:browseDomainsArray];
1115 err = WriteBrowseDomain((CFDataRef)browseDomainData);
1116 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", err);
1117 currentBrowseDomainsArray = [browseDomainsArray copy];
1120 // Save registration domain.
1121 if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) {
1123 err = WriteRegistrationDomain((CFDataRef)[self dataForDomain:regDomainString isEnabled:[wideAreaCheckBox state]]);
1124 if (err != noErr) NSLog(@"WriteRegistrationDomain returned %d\n", err);
1126 if (currentRegDomain) CFRelease(currentRegDomain);
1127 currentRegDomain = [regDomainString copy];
1129 if ([currentRegDomain length] > 0) {
1130 currentWideAreaState = [wideAreaCheckBox state];
1131 [registrationDataSource removeObject:regDomainString];
1132 [registrationDataSource addObject:currentRegDomain];
1133 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
1134 [regDomainsComboBox reloadData];
1136 currentWideAreaState = NO;
1137 [self toggleWideAreaBonjour:NO];
1138 if (defaultRegDomain != nil) [regDomainsComboBox setStringValue:defaultRegDomain];
1140 } else if (regSecretWasSet) {
1141 WriteRegistrationDomain((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1142 usleep(200000); // Temporary hack
1143 if ([currentRegDomain length] > 0) WriteRegistrationDomain((CFDataRef)[self dataForDomain:currentRegDomain isEnabled:currentWideAreaState]);
1148 - (NSPreferencePaneUnselectReply)shouldUnselect
1151 if (prefsNeedUpdating == YES) {
1153 [self disableControls];
1156 @"Apply Configuration Changes?",
1162 @selector( savePanelWillClose:returnCode:contextInfo: ),
1164 (void *) self, // sender,
1166 return NSUnselectLater;
1170 return NSUnselectNow;
1174 -(void)disableControls
1176 [hostName setEnabled:NO];
1177 [hostNameSharedSecretButton setEnabled:NO];
1178 [browseDomainsComboBox setEnabled:NO];
1179 [applyButton setEnabled:NO];
1180 [revertButton setEnabled:NO];
1181 [wideAreaCheckBox setEnabled:NO];
1182 [regDomainsComboBox setEnabled:NO];
1183 [registrationSharedSecretButton setEnabled:NO];
1184 [statusImageView setEnabled:NO];
1186 browseDomainListEnabled = NO;
1187 [browseDomainList deselectAll:self];
1188 [browseDomainList setEnabled:NO];
1190 [addBrowseDomainButton setEnabled:NO];
1191 [removeBrowseDomainButton setEnabled:NO];
1195 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
1197 (void)row; // Unused
1198 (void)tableView; // Unused
1199 return browseDomainListEnabled;
1203 -(void)enableControls
1205 [hostName setEnabled:YES];
1206 [hostNameSharedSecretButton setEnabled:YES];
1207 [browseDomainsComboBox setEnabled:YES];
1208 [wideAreaCheckBox setEnabled:YES];
1209 [registrationSharedSecretButton setEnabled:YES];
1210 [self toggleWideAreaBonjour:[wideAreaCheckBox state]];
1211 [statusImageView setEnabled:YES];
1212 [addBrowseDomainButton setEnabled:YES];
1214 [browseDomainList setEnabled:YES];
1215 [browseDomainList deselectAll:self];
1216 browseDomainListEnabled = YES;
1218 [removeBrowseDomainButton setEnabled:[browseDomainList numberOfSelectedRows]];
1219 [applyButton setEnabled:prefsNeedUpdating];
1220 [revertButton setEnabled:prefsNeedUpdating];
1224 - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view
1226 (void)view; // Unused
1227 [self enableControls];
1231 - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view
1233 (void)view; // Unused
1234 [self disableControls];
1240 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
1241 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
1242 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
1243 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
1244 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
1246 // NOT static -- otherwise the compiler may optimize it out
1247 // The "@(#) " pattern is a special prefix the "what" command looks for
1248 const char VersionString_SCCS[] = "@(#) Bonjour Preference Pane " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
1250 // If the process crashes, then this string will be magically included in the automatically-generated crash log
1251 const char *__crashreporter_info__ = VersionString_SCCS + 5;
1252 asm(".desc ___crashreporter_info__, 0x10");