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.11 2007/11/30 23:42:09 cheshire
47 Fixed compile warning: declaration of 'index' shadows a global declaration
49 Revision 1.10 2007/09/18 19:09:02 cheshire
50 <rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings
52 Revision 1.9 2007/02/09 00:39:06 cheshire
55 Revision 1.8 2006/08/14 23:15:47 cheshire
56 Tidy up Change History comment
58 Revision 1.7 2006/07/14 03:59:14 cheshire
59 Fix compile warnings: 'sortUsingFunction:context:' comparison function needs to return int
61 Revision 1.6 2005/02/26 00:44:24 cheshire
62 Restore default reg domain if user deletes text and clicks "apply"
64 Revision 1.5 2005/02/25 02:29:28 cheshire
65 Show yellow dot for "update in progress"
67 Revision 1.4 2005/02/16 00:18:33 cheshire
70 Revision 1.3 2005/02/10 22:35:20 cheshire
71 <rdar://problem/3727944> Update name
73 Revision 1.2 2005/02/08 01:32:05 cheshire
74 Add trimCharactersFromDomain routine to strip leading and trailing
75 white space and punctuation from user-entered fields.
77 Revision 1.1 2005/02/05 01:59:19 cheshire
78 Add Preference Pane to facilitate testing of DDNS & wide-area features
82 #import "DNSServiceDiscoveryPref.h"
83 #import "ConfigurationAuthority.h"
84 #import "PrivilegedOperations.h"
87 @implementation DNSServiceDiscoveryPref
90 MyArrayCompareFunction(id val1, id val2, void *context)
92 (void)context; // Unused
93 return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive);
98 MyDomainArrayCompareFunction(id val1, id val2, void *context)
100 (void)context; // Unused
101 NSString *domain1 = [val1 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
102 NSString *domain2 = [val2 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
103 return CFStringCompare((CFStringRef)domain1, (CFStringRef)domain2, kCFCompareCaseInsensitive);
108 GetNextLabel(const char *cstr, char label[64])
111 while (*cstr && *cstr != '.') // While we have characters in the label...
117 if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1]))
119 int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal
120 int v1 = cstr[ 0] - '0';
121 int v2 = cstr[ 1] - '0';
122 int val = v0 * 100 + v1 * 10 + v2;
123 if (val <= 255) { c = (char)val; cstr += 2; } // If valid three-digit decimal value, use it
127 if (ptr >= label+64) return(NULL);
129 if (*cstr) cstr++; // Skip over the trailing dot (if present)
135 static void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
137 (void)store; // Unused
138 (void)changedKeys; // Unused
139 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
142 [me setupInitialValues];
146 static void ServiceDomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
147 DNSServiceErrorType errorCode, const char *replyDomain, void *context, DNSServiceFlags enumType)
149 (void)sdRef; // Unused
150 (void)interfaceIndex; // Unused
151 (void)errorCode; // Unused
152 if (strcmp(replyDomain, "local.") == 0) return; // local domain is not interesting
154 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
155 BOOL moreComing = (BOOL)(flags & kDNSServiceFlagsMoreComing);
156 NSMutableArray * domainArray;
157 NSMutableArray * defaultBrowseDomainsArray = nil;
158 NSComboBox * domainComboBox;
159 NSString * domainString;
160 NSString * currentDomain = nil;
161 char decodedDomainString[kDNSServiceMaxDomainName] = "\0";
162 char nextLabel[256] = "\0";
163 char * buffer = (char *)replyDomain;
166 buffer = (char *)GetNextLabel(buffer, nextLabel);
167 strcat(decodedDomainString, nextLabel);
168 strcat(decodedDomainString, ".");
171 // Remove trailing dot from domain name.
172 decodedDomainString[strlen(decodedDomainString)-1] = '\0';
174 domainString = [[[NSString alloc] initWithUTF8String:(const char *)decodedDomainString] autorelease];
176 if (enumType & kDNSServiceFlagsRegistrationDomains) {
177 domainArray = [me registrationDataSource];
178 domainComboBox = [me regDomainsComboBox];
179 currentDomain = [me currentRegDomain];
181 domainArray = [me browseDataSource];
182 domainComboBox = [me browseDomainsComboBox];
183 defaultBrowseDomainsArray = [me defaultBrowseDomainsArray];
186 if (flags & kDNSServiceFlagsAdd) {
187 [domainArray removeObject:domainString]; // How can I check if an object is in the array?
188 [domainArray addObject:domainString];
189 if ((flags & kDNSServiceFlagsDefault) && (enumType & kDNSServiceFlagsRegistrationDomains)) {
190 [me setDefaultRegDomain:domainString];
191 if ([[domainComboBox stringValue] length] == 0) [domainComboBox setStringValue:domainString];
192 } else if ((flags & kDNSServiceFlagsDefault) && !(enumType & kDNSServiceFlagsRegistrationDomains)) {
193 [defaultBrowseDomainsArray removeObject:domainString];
194 [defaultBrowseDomainsArray addObject:domainString];
198 if (moreComing == NO) {
199 [domainArray sortUsingFunction:MyArrayCompareFunction context:nil];
200 [domainComboBox reloadData];
206 browseDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
207 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
209 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsBrowseDomains);
214 registrationDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
215 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
217 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsRegistrationDomains);
223 MyDNSServiceCleanUp(MyDNSServiceState * query)
225 /* Remove the CFRunLoopSource from the current run loop. */
226 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
227 CFRelease(query->source);
229 /* Invalidate the CFSocket. */
230 CFSocketInvalidate(query->socket);
231 CFRelease(query->socket);
233 /* Workaround that gives time to CFSocket's select thread so it can remove the socket from its FD set
234 before we close the socket by calling DNSServiceRefDeallocate. <rdar://problem/3585273> */
237 /* Terminate the connection with the mDNSResponder daemon, which cancels the query. */
238 DNSServiceRefDeallocate(query->service);
244 MySocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void * data, void * info)
248 #pragma unused(address)
251 DNSServiceErrorType err;
253 MyDNSServiceState * query = (MyDNSServiceState *)info; // context passed in to CFSocketCreateWithNative().
254 assert(query != NULL);
256 /* Read a reply from the mDNSResponder. */
257 err= DNSServiceProcessResult(query->service);
258 if (err != kDNSServiceErr_NoError) {
259 fprintf(stderr, "DNSServiceProcessResult returned %d\n", err);
261 /* Terminate the query operation and release the CFRunLoopSource and CFSocket. */
262 MyDNSServiceCleanUp(query);
269 MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query)
271 CFSocketNativeHandle sock;
272 CFOptionFlags sockFlags;
273 CFSocketContext context = { 0, query, NULL, NULL, NULL }; // Use MyDNSServiceState as context data.
275 /* Access the underlying Unix domain socket to communicate with the mDNSResponder daemon. */
276 sock = DNSServiceRefSockFD(query->service);
279 /* Create a CFSocket using the Unix domain socket. */
280 query->socket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, MySocketReadCallback, &context);
281 assert(query->socket != NULL);
283 /* Prevent CFSocketInvalidate from closing DNSServiceRef's socket. */
284 sockFlags = CFSocketGetSocketFlags(query->socket);
285 CFSocketSetSocketFlags(query->socket, sockFlags & (~kCFSocketCloseOnInvalidate));
287 /* Create a CFRunLoopSource from the CFSocket. */
288 query->source = CFSocketCreateRunLoopSource(NULL, query->socket, 0);
289 assert(query->source != NULL);
291 /* Add the CFRunLoopSource to the current run loop. */
292 CFRunLoopAddSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
297 -(void)updateStatusImageView
299 int value = [self statusForHostName:currentHostName];
300 if (value == 0) [statusImageView setImage:successImage];
301 else if (value > 0) [statusImageView setImage:inprogressImage];
302 else [statusImageView setImage:failureImage];
306 - (void)watchForPreferenceChanges
308 SCDynamicStoreContext context = { 0, self, NULL, NULL, NULL };
309 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("watchForPreferenceChanges"), NetworkChanged, &context);
310 CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
311 CFRunLoopSourceRef rls;
313 assert(store != NULL);
314 assert(keys != NULL);
316 CFArrayAppendValue(keys, SC_DYNDNS_STATE_KEY);
317 CFArrayAppendValue(keys, SC_DYNDNS_SETUP_KEY);
319 (void)SCDynamicStoreSetNotificationKeys(store, keys, NULL);
321 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
324 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
331 -(int)statusForHostName:(NSString * )domain
333 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("statusForHostName"), NULL, NULL);
334 NSString *lowercaseDomain = [domain lowercaseString];
337 assert(store != NULL);
339 NSDictionary *dynamicDNS = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY);
341 NSDictionary *hostNames = [dynamicDNS objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
342 NSDictionary *infoDict = [hostNames objectForKey:lowercaseDomain];
343 if (infoDict) status = [[infoDict objectForKey:(NSString*)SC_DYNDNS_STATUS_KEY] intValue];
344 CFRelease(dynamicDNS);
352 - (void)startDomainBrowsing
354 DNSServiceFlags flags;
355 OSStatus err = noErr;
357 flags = kDNSServiceFlagsRegistrationDomains;
358 err = DNSServiceEnumerateDomains(®Query.service, flags, 0, registrationDomainReply, (void *)self);
359 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(®Query);
361 flags = kDNSServiceFlagsBrowseDomains;
362 err = DNSServiceEnumerateDomains(&browseQuery.service, flags, 0, browseDomainReply, (void *)self);
363 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(&browseQuery);
367 -(void)readPreferences
369 NSDictionary *origDict;
370 NSArray *regDomainArray;
373 if (currentRegDomain) [currentRegDomain release];
374 if (currentBrowseDomainsArray) [currentBrowseDomainsArray release];
375 if (currentHostName) [currentHostName release];
377 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL);
378 origDict = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY);
380 regDomainArray = [origDict objectForKey:(NSString *)SC_DYNDNS_REGDOMAINS_KEY];
381 if (regDomainArray && [regDomainArray count] > 0) {
382 currentRegDomain = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
383 currentWideAreaState = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
385 currentRegDomain = [[NSString alloc] initWithString:@""];
386 currentWideAreaState = NO;
389 currentBrowseDomainsArray = [[origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY] retain];
391 hostArray = [origDict objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
392 if (hostArray && [hostArray count] > 0) {
393 currentHostName = [[[hostArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
395 currentHostName = [[NSString alloc] initWithString:@""];
403 - (void)tableViewSelectionDidChange:(NSNotification *)notification;
405 [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]];
409 - (void)setBrowseDomainsComboBox;
411 NSString * domain = nil;
413 if ([defaultBrowseDomainsArray count] > 0) {
414 NSEnumerator * arrayEnumerator = [defaultBrowseDomainsArray objectEnumerator];
415 while ((domain = [arrayEnumerator nextObject]) != NULL) {
416 if ([self domainAlreadyInList:domain] == NO) break;
419 if (domain) [browseDomainsComboBox setStringValue:domain];
420 else [browseDomainsComboBox setStringValue:@""];
424 - (IBAction)addBrowseDomainClicked:(id)sender;
426 [self setBrowseDomainsComboBox];
428 [NSApp beginSheet:addBrowseDomainWindow modalForWindow:mainWindow modalDelegate:self
429 didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
431 [browseDomainList deselectAll:sender];
432 [self updateApplyButtonState];
436 - (IBAction)removeBrowseDomainClicked:(id)sender;
438 (void)sender; // Unused
439 int selectedBrowseDomain = [browseDomainList selectedRow];
440 [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain];
441 [browseDomainList reloadData];
442 [self updateApplyButtonState];
446 - (IBAction)enableBrowseDomainClicked:(id)sender;
448 NSTableView *tableView = sender;
449 NSMutableDictionary *browseDomainDict;
452 browseDomainDict = [[browseDomainsArray objectAtIndex:[tableView clickedRow]] mutableCopy];
453 value = [[browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
454 [browseDomainDict setObject:[[[NSNumber alloc] initWithInt:(!value)] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
455 [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict];
456 [tableView reloadData];
457 [self updateApplyButtonState];
462 - (int)numberOfRowsInTableView:(NSTableView *)tableView;
464 (void)tableView; // Unused
465 int numberOfRows = 0;
467 if (browseDomainsArray) {
468 numberOfRows = [browseDomainsArray count];
474 - (void)tabView:(NSTabView *)xtabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem;
476 (void)xtabView; // Unused
477 (void)tabViewItem; // Unused
478 [browseDomainList deselectAll:self];
479 [mainWindow makeFirstResponder:nil];
483 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row;
485 (void)tableView; // Unused
486 NSDictionary *browseDomainDict;
489 if (browseDomainsArray) {
490 browseDomainDict = [browseDomainsArray objectAtIndex:row];
491 if (browseDomainDict) {
492 if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_ENABLED_KEY]) {
493 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
494 } else if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_DOMAIN_KEY]) {
495 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
503 - (void)setupInitialValues
505 [self readPreferences];
507 if (currentHostName) {
508 [hostName setStringValue:currentHostName];
509 [self updateStatusImageView];
512 if (browseDomainsArray) {
513 [browseDomainsArray release];
514 browseDomainsArray = nil;
517 if (currentBrowseDomainsArray) {
518 browseDomainsArray = [currentBrowseDomainsArray mutableCopy];
519 if (browseDomainsArray) {
520 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
521 if ([browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
522 OSStatus err = WriteBrowseDomain((CFDataRef)[self dataForDomainArray:browseDomainsArray]);
523 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", err);
524 [currentBrowseDomainsArray release];
525 currentBrowseDomainsArray = [browseDomainsArray copy];
529 browseDomainsArray = nil;
531 [browseDomainList reloadData];
533 if (currentRegDomain && ([currentRegDomain length] > 0)) {
534 [regDomainsComboBox setStringValue:currentRegDomain];
535 [registrationDataSource removeObject:currentRegDomain];
536 [registrationDataSource addObject:currentRegDomain];
537 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
538 [regDomainsComboBox reloadData];
541 if (currentWideAreaState) {
542 [self toggleWideAreaBonjour:YES];
544 [self toggleWideAreaBonjour:NO];
547 if (hostNameSharedSecretValue) {
548 [hostNameSharedSecretValue release];
549 hostNameSharedSecretValue = nil;
552 if (regSharedSecretValue) {
553 [regSharedSecretValue release];
554 regSharedSecretValue = nil;
557 [self updateApplyButtonState];
558 [mainWindow makeFirstResponder:nil];
559 [browseDomainList deselectAll:self];
560 [removeBrowseDomainButton setEnabled:NO];
569 prefsNeedUpdating = NO;
571 browseDomainListEnabled = NO;
572 defaultRegDomain = nil;
573 currentRegDomain = nil;
574 currentBrowseDomainsArray = nil;
575 currentHostName = nil;
576 hostNameSharedSecretValue = nil;
577 regSharedSecretValue = nil;
578 browseDomainsArray = nil;
579 justStartedEditing = YES;
580 currentWideAreaState = NO;
581 NSString *successPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"success" ofType:@"tiff"];
582 NSString *inprogressPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"inprogress" ofType:@"tiff"];
583 NSString *failurePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"failure" ofType:@"tiff"];
585 registrationDataSource = [[NSMutableArray alloc] init];
586 browseDataSource = [[NSMutableArray alloc] init];
587 defaultBrowseDomainsArray = [[NSMutableArray alloc] init];
588 successImage = [[NSImage alloc] initWithContentsOfFile:successPath];
589 inprogressImage = [[NSImage alloc] initWithContentsOfFile:inprogressPath];
590 failureImage = [[NSImage alloc] initWithContentsOfFile:failurePath];
592 [tabView selectFirstTabViewItem:self];
593 [self setupInitialValues];
594 [self startDomainBrowsing];
595 [self watchForPreferenceChanges];
597 [tabView setDelegate:self];
599 InitConfigAuthority();
600 err = EnsureToolInstalled();
601 if (err == noErr) toolInstalled = YES;
602 else fprintf(stderr, "EnsureToolInstalled returned %ld\n", err);
607 - (IBAction)closeMyCustomSheet:(id)sender
609 BOOL result = [sender isEqualTo:browseOKButton] || [sender isEqualTo:secretOKButton];
611 if (result) [NSApp endSheet:[sender window] returnCode:NSOKButton];
612 else [NSApp endSheet:[sender window] returnCode:NSCancelButton];
616 - (void)sharedSecretSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
618 NSButton * button = (NSButton *)contextInfo;
619 [sheet orderOut:self];
620 [self enableControls];
622 if (returnCode == NSOKButton) {
623 if ([button isEqualTo:hostNameSharedSecretButton]) {
624 hostNameSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
625 hostNameSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
627 regSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
628 regSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
630 [self updateApplyButtonState];
632 [sharedSecretValue setStringValue:@""];
636 - (BOOL)domainAlreadyInList:(NSString *)domainString
638 if (browseDomainsArray) {
639 NSDictionary *domainDict;
640 NSString *domainName;
641 NSEnumerator *arrayEnumerator = [browseDomainsArray objectEnumerator];
642 while ((domainDict = [arrayEnumerator nextObject]) != NULL) {
643 domainName = [domainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
644 if ([domainString caseInsensitiveCompare:domainName] == NSOrderedSame) return YES;
651 - (NSString *)trimCharactersFromDomain:(NSString *)domain
653 NSMutableCharacterSet * trimSet = [[[NSCharacterSet whitespaceCharacterSet] mutableCopy] autorelease];
654 [trimSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
655 return [domain stringByTrimmingCharactersInSet:trimSet];
659 - (void)addBrowseDomainSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
661 (void)contextInfo; // Unused
662 [sheet orderOut:self];
663 [self enableControls];
665 if (returnCode == NSOKButton) {
666 NSString * newBrowseDomainString = [self trimCharactersFromDomain:[browseDomainsComboBox stringValue]];
667 NSMutableDictionary *newBrowseDomainDict;
669 if (browseDomainsArray == nil) browseDomainsArray = [[NSMutableArray alloc] initWithCapacity:0];
670 if ([self domainAlreadyInList:newBrowseDomainString] == NO) {
671 newBrowseDomainDict = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
673 [newBrowseDomainDict setObject:newBrowseDomainString forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
674 [newBrowseDomainDict setObject:[[[NSNumber alloc] initWithBool:YES] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
676 [browseDomainsArray addObject:newBrowseDomainDict];
677 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
678 [browseDomainList reloadData];
679 [self updateApplyButtonState];
685 -(void)validateTextFields
687 [hostName validateEditing];
688 [browseDomainsComboBox validateEditing];
689 [regDomainsComboBox validateEditing];
693 - (IBAction)changeButtonPressed:(id)sender
697 [self disableControls];
698 [self validateTextFields];
699 [mainWindow makeFirstResponder:nil];
700 [browseDomainList deselectAll:sender];
702 if ([sender isEqualTo:hostNameSharedSecretButton]) {
703 if (hostNameSharedSecretValue) {
704 [sharedSecretValue setStringValue:hostNameSharedSecretValue];
705 } else if ((keyName = [self sharedSecretKeyName:[hostName stringValue]]) != NULL) {
706 [sharedSecretName setStringValue:keyName];
707 [sharedSecretValue setStringValue:@"****************"];
709 [sharedSecretName setStringValue:[hostName stringValue]];
710 [sharedSecretValue setStringValue:@""];
714 if (regSharedSecretValue) {
715 [sharedSecretValue setStringValue:regSharedSecretValue];
716 } else if ((keyName = [self sharedSecretKeyName:[regDomainsComboBox stringValue]]) != NULL) {
717 [sharedSecretName setStringValue:keyName];
718 [sharedSecretValue setStringValue:@"****************"];
720 [sharedSecretName setStringValue:[regDomainsComboBox stringValue]];
721 [sharedSecretValue setStringValue:@""];
725 [sharedSecretWindow resignFirstResponder];
727 if ([[sharedSecretName stringValue] length] > 0) [sharedSecretWindow makeFirstResponder:sharedSecretValue];
728 else [sharedSecretWindow makeFirstResponder:sharedSecretName];
730 [NSApp beginSheet:sharedSecretWindow modalForWindow:mainWindow modalDelegate:self
731 didEndSelector:@selector(sharedSecretSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
735 - (IBAction)wideAreaCheckBoxChanged:(id)sender
737 [self toggleWideAreaBonjour:[sender state]];
738 [self updateApplyButtonState];
739 [mainWindow makeFirstResponder:nil];
743 - (void)updateApplyButtonState
745 NSString *hostNameString = [hostName stringValue];
746 NSString *regDomainString = [regDomainsComboBox stringValue];
748 NSComparisonResult hostNameResult = [hostNameString compare:currentHostName];
749 NSComparisonResult regDomainResult = [regDomainString compare:currentRegDomain];
751 if ((currentHostName && (hostNameResult != NSOrderedSame)) ||
752 (currentRegDomain && (regDomainResult != NSOrderedSame) && ([wideAreaCheckBox state])) ||
753 (currentHostName == nil && ([hostNameString length]) > 0) ||
754 (currentRegDomain == nil && ([regDomainString length]) > 0) ||
755 (currentWideAreaState != [wideAreaCheckBox state]) ||
756 (hostNameSharedSecretValue != nil) ||
757 (regSharedSecretValue != nil) ||
758 (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO))
760 [self enableApplyButton];
762 [self disableApplyButton];
768 - (void)controlTextDidChange:(NSNotification *)notification;
770 (void)notification; // Unused
771 [self updateApplyButtonState];
776 - (IBAction)comboAction:(id)sender;
778 (void)sender; // Unused
779 [self updateApplyButtonState];
783 - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)ind
785 NSString *domain = nil;
786 if ([aComboBox isEqualTo:browseDomainsComboBox]) domain = [browseDataSource objectAtIndex:ind];
787 else if ([aComboBox isEqualTo:regDomainsComboBox]) domain = [registrationDataSource objectAtIndex:ind];
793 - (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox
796 if ([aComboBox isEqualTo:browseDomainsComboBox]) count = [browseDataSource count];
797 else if ([aComboBox isEqualTo:regDomainsComboBox]) count = [registrationDataSource count];
802 - (NSMutableArray *)browseDataSource
804 return browseDataSource;
808 - (NSMutableArray *)registrationDataSource
810 return registrationDataSource;
814 - (NSComboBox *)browseDomainsComboBox
816 return browseDomainsComboBox;
820 - (NSComboBox *)regDomainsComboBox
822 return regDomainsComboBox;
826 - (NSString *)currentRegDomain
828 return currentRegDomain;
832 - (NSMutableArray *)defaultBrowseDomainsArray
834 return defaultBrowseDomainsArray;
838 - (NSArray *)currentBrowseDomainsArray
840 return currentBrowseDomainsArray;
844 - (NSString *)currentHostName
846 return currentHostName;
850 - (NSString *)defaultRegDomain
852 return defaultRegDomain;
856 - (void)setDefaultRegDomain:(NSString *)domain
858 [defaultRegDomain release];
859 defaultRegDomain = domain;
860 [defaultRegDomain retain];
867 mainWindow = [[self mainView] window];
871 - (void)mainViewDidLoad
873 [comboAuthButton setString:"system.preferences"];
874 [comboAuthButton setDelegate:self];
875 [comboAuthButton updateStatus:nil];
876 [comboAuthButton setAutoupdate:YES];
881 - (IBAction)applyClicked:(id)sender
883 (void)sender; // Unused
884 [self applyCurrentState];
888 - (void)applyCurrentState
890 [self validateTextFields];
892 if (toolInstalled == YES) {
893 [self savePreferences];
894 [self disableApplyButton];
895 [mainWindow makeFirstResponder:nil];
900 - (void)enableApplyButton
902 [applyButton setEnabled:YES];
903 [revertButton setEnabled:YES];
904 prefsNeedUpdating = YES;
908 - (void)disableApplyButton
910 [applyButton setEnabled:NO];
911 [revertButton setEnabled:NO];
912 prefsNeedUpdating = NO;
916 - (void)toggleWideAreaBonjour:(BOOL)state
918 [wideAreaCheckBox setState:state];
919 [regDomainsComboBox setEnabled:state];
920 [registrationSharedSecretButton setEnabled:state];
924 - (IBAction)revertClicked:(id)sender;
926 [self restorePreferences];
927 [browseDomainList deselectAll:sender];
928 [mainWindow makeFirstResponder:nil];
932 - (void)restorePreferences
934 [self setupInitialValues];
938 - (void)savePanelWillClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
940 (void)sheet; // Unused
941 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)contextInfo;
943 if (returnCode == NSAlertDefaultReturn) {
944 [me applyCurrentState];
945 } else if (returnCode == NSAlertAlternateReturn ) {
946 [me restorePreferences];
950 [me replyToShouldUnselect:(returnCode != NSAlertOtherReturn)];
954 -(SecKeychainItemRef)copyKeychainItemforDomain:(NSString *)domain
956 const char * serviceName = [domain UTF8String];
957 UInt32 type = 'ddns';
958 UInt32 typeLength = sizeof(type);
960 SecKeychainAttribute attrs[] = { { kSecServiceItemAttr, strlen(serviceName), (char *)serviceName },
961 { kSecTypeItemAttr, typeLength, (UInt32 *)&type } };
963 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
964 SecKeychainSearchRef searchRef;
965 SecKeychainItemRef itemRef = NULL;
968 err = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef);
970 err = SecKeychainSearchCopyNext(searchRef, &itemRef);
971 if (err != noErr) itemRef = NULL;
977 -(NSString *)sharedSecretKeyName:(NSString * )domain
979 SecKeychainItemRef itemRef = NULL;
980 NSString *keyName = nil;
983 err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
984 assert(err == noErr);
986 itemRef = [self copyKeychainItemforDomain:[domain lowercaseString]];
989 SecKeychainAttributeInfo attrInfo;
990 SecKeychainAttributeList *attrList = NULL;
991 SecKeychainAttribute attribute;
994 tags[0] = kSecAccountItemAttr;
997 attrInfo.format = NULL;
999 err = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, NULL, NULL);
1001 for (i = 0; i < attrList->count; i++) {
1002 attribute = attrList->attr[i];
1003 if (attribute.tag == kSecAccountItemAttr) {
1004 keyName = [[NSString alloc] initWithBytes:attribute.data length:attribute.length encoding:NSUTF8StringEncoding];
1008 if (attrList) (void)SecKeychainItemFreeAttributesAndData(attrList, NULL);
1016 -(NSString *)domainForHostName:(NSString *)hostNameString
1018 NSString * domainName = nil;
1022 ptr = (char *)[hostNameString UTF8String];
1024 ptr = (char *)GetNextLabel(ptr, text);
1025 domainName = [[NSString alloc] initWithUTF8String:(const char *)ptr];
1027 return ([domainName autorelease]);
1031 - (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled
1033 NSMutableArray *domainsArray;
1034 NSMutableDictionary *domainDict = nil;
1036 if (domainName && [domainName length] > 0) {
1037 domainDict= [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
1038 [domainDict setObject:domainName forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
1039 [domainDict setObject:[[[NSNumber alloc] initWithBool:enabled] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
1041 domainsArray = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
1042 if (domainDict) [domainsArray addObject:domainDict];
1043 return [NSArchiver archivedDataWithRootObject:domainsArray];
1047 - (NSData *)dataForDomainArray:(NSArray *)domainArray
1049 return [NSArchiver archivedDataWithRootObject:domainArray];
1053 - (NSData *)dataForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName
1055 NSMutableDictionary *sharedSecretDict = [[[NSMutableDictionary alloc] initWithCapacity:3] autorelease];
1056 [sharedSecretDict setObject:secret forKey:(NSString *)SC_DYNDNS_SECRET_KEY];
1057 [sharedSecretDict setObject:[domainName lowercaseString] forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
1058 [sharedSecretDict setObject:keyName forKey:(NSString *)SC_DYNDNS_KEYNAME_KEY];
1059 return [NSArchiver archivedDataWithRootObject:sharedSecretDict];
1063 -(void)savePreferences
1065 NSString *hostNameString = [hostName stringValue];
1066 NSString *browseDomainString = [browseDomainsComboBox stringValue];
1067 NSString *regDomainString = [regDomainsComboBox stringValue];
1068 NSString *tempHostNameSharedSecretName = hostNameSharedSecretName;
1069 NSString *tempRegSharedSecretName = regSharedSecretName;
1070 NSData *browseDomainData = nil;
1071 BOOL regSecretWasSet = NO;
1072 BOOL hostSecretWasSet = NO;
1073 OSStatus err = noErr;
1075 hostNameString = [self trimCharactersFromDomain:hostNameString];
1076 browseDomainString = [self trimCharactersFromDomain:browseDomainString];
1077 regDomainString = [self trimCharactersFromDomain:regDomainString];
1078 tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName];
1079 tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName];
1081 [hostName setStringValue:hostNameString];
1082 [regDomainsComboBox setStringValue:regDomainString];
1084 // Convert Shared Secret account names to lowercase.
1085 tempHostNameSharedSecretName = [tempHostNameSharedSecretName lowercaseString];
1086 tempRegSharedSecretName = [tempRegSharedSecretName lowercaseString];
1088 // Save hostname shared secret.
1089 if ([hostNameSharedSecretName length] > 0 && ([hostNameSharedSecretValue length] > 0)) {
1090 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:hostNameSharedSecretValue domain:hostNameString key:tempHostNameSharedSecretName]);
1091 [hostNameSharedSecretValue release];
1092 hostNameSharedSecretValue = nil;
1093 hostSecretWasSet = YES;
1096 // Save registration domain shared secret.
1097 if (([regSharedSecretName length] > 0) && ([regSharedSecretValue length] > 0)) {
1098 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:regSharedSecretValue domain:regDomainString key:tempRegSharedSecretName]);
1099 [regSharedSecretValue release];
1100 regSharedSecretValue = nil;
1101 regSecretWasSet = YES;
1105 if ((currentHostName == NULL) || [currentHostName compare:hostNameString] != NSOrderedSame) {
1106 err = WriteHostname((CFDataRef)[self dataForDomain:hostNameString isEnabled:YES]);
1107 if (err != noErr) NSLog(@"WriteHostname returned %d\n", err);
1108 currentHostName = [hostNameString copy];
1109 } else if (hostSecretWasSet) {
1110 WriteHostname((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1111 usleep(200000); // Temporary hack
1112 if ([currentHostName length] > 0) WriteHostname((CFDataRef)[self dataForDomain:(NSString *)currentHostName isEnabled:YES]);
1115 // Save browse domain.
1116 if (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
1117 browseDomainData = [self dataForDomainArray:browseDomainsArray];
1118 err = WriteBrowseDomain((CFDataRef)browseDomainData);
1119 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", err);
1120 currentBrowseDomainsArray = [browseDomainsArray copy];
1123 // Save registration domain.
1124 if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) {
1126 err = WriteRegistrationDomain((CFDataRef)[self dataForDomain:regDomainString isEnabled:[wideAreaCheckBox state]]);
1127 if (err != noErr) NSLog(@"WriteRegistrationDomain returned %d\n", err);
1129 if (currentRegDomain) CFRelease(currentRegDomain);
1130 currentRegDomain = [regDomainString copy];
1132 if ([currentRegDomain length] > 0) {
1133 currentWideAreaState = [wideAreaCheckBox state];
1134 [registrationDataSource removeObject:regDomainString];
1135 [registrationDataSource addObject:currentRegDomain];
1136 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
1137 [regDomainsComboBox reloadData];
1139 currentWideAreaState = NO;
1140 [self toggleWideAreaBonjour:NO];
1141 if (defaultRegDomain != nil) [regDomainsComboBox setStringValue:defaultRegDomain];
1143 } else if (regSecretWasSet) {
1144 WriteRegistrationDomain((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1145 usleep(200000); // Temporary hack
1146 if ([currentRegDomain length] > 0) WriteRegistrationDomain((CFDataRef)[self dataForDomain:currentRegDomain isEnabled:currentWideAreaState]);
1151 - (NSPreferencePaneUnselectReply)shouldUnselect
1154 if (prefsNeedUpdating == YES) {
1156 [self disableControls];
1159 @"Apply Configuration Changes?",
1165 @selector( savePanelWillClose:returnCode:contextInfo: ),
1167 (void *) self, // sender,
1169 return NSUnselectLater;
1173 return NSUnselectNow;
1177 -(void)disableControls
1179 [hostName setEnabled:NO];
1180 [hostNameSharedSecretButton setEnabled:NO];
1181 [browseDomainsComboBox setEnabled:NO];
1182 [applyButton setEnabled:NO];
1183 [revertButton setEnabled:NO];
1184 [wideAreaCheckBox setEnabled:NO];
1185 [regDomainsComboBox setEnabled:NO];
1186 [registrationSharedSecretButton setEnabled:NO];
1187 [statusImageView setEnabled:NO];
1189 browseDomainListEnabled = NO;
1190 [browseDomainList deselectAll:self];
1191 [browseDomainList setEnabled:NO];
1193 [addBrowseDomainButton setEnabled:NO];
1194 [removeBrowseDomainButton setEnabled:NO];
1198 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
1200 (void)row; // Unused
1201 (void)tableView; // Unused
1202 return browseDomainListEnabled;
1206 -(void)enableControls
1208 [hostName setEnabled:YES];
1209 [hostNameSharedSecretButton setEnabled:YES];
1210 [browseDomainsComboBox setEnabled:YES];
1211 [wideAreaCheckBox setEnabled:YES];
1212 [registrationSharedSecretButton setEnabled:YES];
1213 [self toggleWideAreaBonjour:[wideAreaCheckBox state]];
1214 [statusImageView setEnabled:YES];
1215 [addBrowseDomainButton setEnabled:YES];
1217 [browseDomainList setEnabled:YES];
1218 [browseDomainList deselectAll:self];
1219 browseDomainListEnabled = YES;
1221 [removeBrowseDomainButton setEnabled:[browseDomainList numberOfSelectedRows]];
1222 [applyButton setEnabled:prefsNeedUpdating];
1223 [revertButton setEnabled:prefsNeedUpdating];
1227 - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view
1229 (void)view; // Unused
1230 [self enableControls];
1234 - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view
1236 (void)view; // Unused
1237 [self disableControls];
1243 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
1244 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
1245 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
1246 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
1247 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
1249 // NOT static -- otherwise the compiler may optimize it out
1250 // The "@(#) " pattern is a special prefix the "what" command looks for
1251 const char VersionString_SCCS[] = "@(#) Bonjour Preference Pane " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
1253 // If the process crashes, then this string will be magically included in the automatically-generated crash log
1254 const char *__crashreporter_info__ = VersionString_SCCS + 5;
1255 asm(".desc ___crashreporter_info__, 0x10");