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.16 2008/09/15 23:52:30 cheshire
47 <rdar://problem/6218902> mDNSResponder-177 fails to compile on Linux with .desc pseudo-op
48 Made __crashreporter_info__ symbol conditional, so we only use it for OS X build
50 Revision 1.15 2008/08/18 17:57:04 mcguire
51 <rdar://problem/6156209> build error
53 Revision 1.14 2008/07/18 17:39:14 cheshire
54 If NSInteger is not defined (indicated by lack of definition for NSINTEGER_DEFINED)
55 then #define "NSInteger" to be "int" like it used to be
57 Revision 1.13 2008/07/01 01:40:01 mcguire
58 <rdar://problem/5823010> 64-bit fixes
60 Revision 1.12 2008/05/08 00:46:38 cheshire
61 <rdar://problem/5919272> GetNextLabel insufficiently defensive
62 User shared copy of GetNextLabel in ClientCommon.c instead of having a local copy here
64 Revision 1.11 2007/11/30 23:42:09 cheshire
65 Fixed compile warning: declaration of 'index' shadows a global declaration
67 Revision 1.10 2007/09/18 19:09:02 cheshire
68 <rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings
70 Revision 1.9 2007/02/09 00:39:06 cheshire
73 Revision 1.8 2006/08/14 23:15:47 cheshire
74 Tidy up Change History comment
76 Revision 1.7 2006/07/14 03:59:14 cheshire
77 Fix compile warnings: 'sortUsingFunction:context:' comparison function needs to return int
79 Revision 1.6 2005/02/26 00:44:24 cheshire
80 Restore default reg domain if user deletes text and clicks "apply"
82 Revision 1.5 2005/02/25 02:29:28 cheshire
83 Show yellow dot for "update in progress"
85 Revision 1.4 2005/02/16 00:18:33 cheshire
88 Revision 1.3 2005/02/10 22:35:20 cheshire
89 <rdar://problem/3727944> Update name
91 Revision 1.2 2005/02/08 01:32:05 cheshire
92 Add trimCharactersFromDomain routine to strip leading and trailing
93 white space and punctuation from user-entered fields.
95 Revision 1.1 2005/02/05 01:59:19 cheshire
96 Add Preference Pane to facilitate testing of DDNS & wide-area features
100 #import "DNSServiceDiscoveryPref.h"
101 #import "ConfigurationAuthority.h"
102 #import "PrivilegedOperations.h"
105 #include "../../Clients/ClientCommon.h"
107 #ifndef NSINTEGER_DEFINED
108 #define NSInteger int
111 @implementation DNSServiceDiscoveryPref
113 static NSComparisonResult
114 MyArrayCompareFunction(id val1, id val2, void *context)
116 (void)context; // Unused
117 return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive);
121 static NSComparisonResult
122 MyDomainArrayCompareFunction(id val1, id val2, void *context)
124 (void)context; // Unused
125 NSString *domain1 = [val1 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
126 NSString *domain2 = [val2 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
127 return CFStringCompare((CFStringRef)domain1, (CFStringRef)domain2, kCFCompareCaseInsensitive);
131 static void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
133 (void)store; // Unused
134 (void)changedKeys; // Unused
135 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
138 [me setupInitialValues];
142 static void ServiceDomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
143 DNSServiceErrorType errorCode, const char *replyDomain, void *context, DNSServiceFlags enumType)
145 (void)sdRef; // Unused
146 (void)interfaceIndex; // Unused
147 (void)errorCode; // Unused
148 if (strcmp(replyDomain, "local.") == 0) return; // local domain is not interesting
150 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
151 BOOL moreComing = (BOOL)(flags & kDNSServiceFlagsMoreComing);
152 NSMutableArray * domainArray;
153 NSMutableArray * defaultBrowseDomainsArray = nil;
154 NSComboBox * domainComboBox;
155 NSString * domainString;
156 NSString * currentDomain = nil;
157 char decodedDomainString[kDNSServiceMaxDomainName] = "\0";
158 char nextLabel[256] = "\0";
159 char * buffer = (char *)replyDomain;
162 buffer = (char *)GetNextLabel(buffer, nextLabel);
163 strcat(decodedDomainString, nextLabel);
164 strcat(decodedDomainString, ".");
167 // Remove trailing dot from domain name.
168 decodedDomainString[strlen(decodedDomainString)-1] = '\0';
170 domainString = [[[NSString alloc] initWithUTF8String:(const char *)decodedDomainString] autorelease];
172 if (enumType & kDNSServiceFlagsRegistrationDomains) {
173 domainArray = [me registrationDataSource];
174 domainComboBox = [me regDomainsComboBox];
175 currentDomain = [me currentRegDomain];
177 domainArray = [me browseDataSource];
178 domainComboBox = [me browseDomainsComboBox];
179 defaultBrowseDomainsArray = [me defaultBrowseDomainsArray];
182 if (flags & kDNSServiceFlagsAdd) {
183 [domainArray removeObject:domainString]; // How can I check if an object is in the array?
184 [domainArray addObject:domainString];
185 if ((flags & kDNSServiceFlagsDefault) && (enumType & kDNSServiceFlagsRegistrationDomains)) {
186 [me setDefaultRegDomain:domainString];
187 if ([[domainComboBox stringValue] length] == 0) [domainComboBox setStringValue:domainString];
188 } else if ((flags & kDNSServiceFlagsDefault) && !(enumType & kDNSServiceFlagsRegistrationDomains)) {
189 [defaultBrowseDomainsArray removeObject:domainString];
190 [defaultBrowseDomainsArray addObject:domainString];
194 if (moreComing == NO) {
195 [domainArray sortUsingFunction:MyArrayCompareFunction context:nil];
196 [domainComboBox reloadData];
202 browseDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
203 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
205 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsBrowseDomains);
210 registrationDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
211 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
213 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsRegistrationDomains);
219 MyDNSServiceCleanUp(MyDNSServiceState * query)
221 /* Remove the CFRunLoopSource from the current run loop. */
222 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
223 CFRelease(query->source);
225 /* Invalidate the CFSocket. */
226 CFSocketInvalidate(query->socket);
227 CFRelease(query->socket);
229 /* Workaround that gives time to CFSocket's select thread so it can remove the socket from its FD set
230 before we close the socket by calling DNSServiceRefDeallocate. <rdar://problem/3585273> */
233 /* Terminate the connection with the mDNSResponder daemon, which cancels the query. */
234 DNSServiceRefDeallocate(query->service);
240 MySocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void * data, void * info)
244 #pragma unused(address)
247 DNSServiceErrorType err;
249 MyDNSServiceState * query = (MyDNSServiceState *)info; // context passed in to CFSocketCreateWithNative().
250 assert(query != NULL);
252 /* Read a reply from the mDNSResponder. */
253 err= DNSServiceProcessResult(query->service);
254 if (err != kDNSServiceErr_NoError) {
255 fprintf(stderr, "DNSServiceProcessResult returned %d\n", err);
257 /* Terminate the query operation and release the CFRunLoopSource and CFSocket. */
258 MyDNSServiceCleanUp(query);
265 MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query)
267 CFSocketNativeHandle sock;
268 CFOptionFlags sockFlags;
269 CFSocketContext context = { 0, query, NULL, NULL, NULL }; // Use MyDNSServiceState as context data.
271 /* Access the underlying Unix domain socket to communicate with the mDNSResponder daemon. */
272 sock = DNSServiceRefSockFD(query->service);
275 /* Create a CFSocket using the Unix domain socket. */
276 query->socket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, MySocketReadCallback, &context);
277 assert(query->socket != NULL);
279 /* Prevent CFSocketInvalidate from closing DNSServiceRef's socket. */
280 sockFlags = CFSocketGetSocketFlags(query->socket);
281 CFSocketSetSocketFlags(query->socket, sockFlags & (~kCFSocketCloseOnInvalidate));
283 /* Create a CFRunLoopSource from the CFSocket. */
284 query->source = CFSocketCreateRunLoopSource(NULL, query->socket, 0);
285 assert(query->source != NULL);
287 /* Add the CFRunLoopSource to the current run loop. */
288 CFRunLoopAddSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
293 -(void)updateStatusImageView
295 int value = [self statusForHostName:currentHostName];
296 if (value == 0) [statusImageView setImage:successImage];
297 else if (value > 0) [statusImageView setImage:inprogressImage];
298 else [statusImageView setImage:failureImage];
302 - (void)watchForPreferenceChanges
304 SCDynamicStoreContext context = { 0, self, NULL, NULL, NULL };
305 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("watchForPreferenceChanges"), NetworkChanged, &context);
306 CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
307 CFRunLoopSourceRef rls;
309 assert(store != NULL);
310 assert(keys != NULL);
312 CFArrayAppendValue(keys, SC_DYNDNS_STATE_KEY);
313 CFArrayAppendValue(keys, SC_DYNDNS_SETUP_KEY);
315 (void)SCDynamicStoreSetNotificationKeys(store, keys, NULL);
317 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
320 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
327 -(int)statusForHostName:(NSString * )domain
329 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("statusForHostName"), NULL, NULL);
330 NSString *lowercaseDomain = [domain lowercaseString];
333 assert(store != NULL);
335 NSDictionary *dynamicDNS = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY);
337 NSDictionary *hostNames = [dynamicDNS objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
338 NSDictionary *infoDict = [hostNames objectForKey:lowercaseDomain];
339 if (infoDict) status = [[infoDict objectForKey:(NSString*)SC_DYNDNS_STATUS_KEY] intValue];
340 CFRelease(dynamicDNS);
348 - (void)startDomainBrowsing
350 DNSServiceFlags flags;
351 OSStatus err = noErr;
353 flags = kDNSServiceFlagsRegistrationDomains;
354 err = DNSServiceEnumerateDomains(®Query.service, flags, 0, registrationDomainReply, (void *)self);
355 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(®Query);
357 flags = kDNSServiceFlagsBrowseDomains;
358 err = DNSServiceEnumerateDomains(&browseQuery.service, flags, 0, browseDomainReply, (void *)self);
359 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(&browseQuery);
363 -(void)readPreferences
365 NSDictionary *origDict;
366 NSArray *regDomainArray;
369 if (currentRegDomain) [currentRegDomain release];
370 if (currentBrowseDomainsArray) [currentBrowseDomainsArray release];
371 if (currentHostName) [currentHostName release];
373 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL);
374 origDict = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY);
376 regDomainArray = [origDict objectForKey:(NSString *)SC_DYNDNS_REGDOMAINS_KEY];
377 if (regDomainArray && [regDomainArray count] > 0) {
378 currentRegDomain = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
379 currentWideAreaState = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
381 currentRegDomain = [[NSString alloc] initWithString:@""];
382 currentWideAreaState = NO;
385 currentBrowseDomainsArray = [[origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY] retain];
387 hostArray = [origDict objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
388 if (hostArray && [hostArray count] > 0) {
389 currentHostName = [[[hostArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
391 currentHostName = [[NSString alloc] initWithString:@""];
399 - (void)tableViewSelectionDidChange:(NSNotification *)notification;
401 [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]];
405 - (void)setBrowseDomainsComboBox;
407 NSString * domain = nil;
409 if ([defaultBrowseDomainsArray count] > 0) {
410 NSEnumerator * arrayEnumerator = [defaultBrowseDomainsArray objectEnumerator];
411 while ((domain = [arrayEnumerator nextObject]) != NULL) {
412 if ([self domainAlreadyInList:domain] == NO) break;
415 if (domain) [browseDomainsComboBox setStringValue:domain];
416 else [browseDomainsComboBox setStringValue:@""];
420 - (IBAction)addBrowseDomainClicked:(id)sender;
422 [self setBrowseDomainsComboBox];
424 [NSApp beginSheet:addBrowseDomainWindow modalForWindow:mainWindow modalDelegate:self
425 didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
427 [browseDomainList deselectAll:sender];
428 [self updateApplyButtonState];
432 - (IBAction)removeBrowseDomainClicked:(id)sender;
434 (void)sender; // Unused
435 int selectedBrowseDomain = [browseDomainList selectedRow];
436 [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain];
437 [browseDomainList reloadData];
438 [self updateApplyButtonState];
442 - (IBAction)enableBrowseDomainClicked:(id)sender;
444 NSTableView *tableView = sender;
445 NSMutableDictionary *browseDomainDict;
448 browseDomainDict = [[browseDomainsArray objectAtIndex:[tableView clickedRow]] mutableCopy];
449 value = [[browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
450 [browseDomainDict setObject:[[[NSNumber alloc] initWithInt:(!value)] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
451 [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict];
452 [tableView reloadData];
453 [self updateApplyButtonState];
458 - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView;
460 (void)tableView; // Unused
461 int numberOfRows = 0;
463 if (browseDomainsArray) {
464 numberOfRows = [browseDomainsArray count];
470 - (void)tabView:(NSTabView *)xtabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem;
472 (void)xtabView; // Unused
473 (void)tabViewItem; // Unused
474 [browseDomainList deselectAll:self];
475 [mainWindow makeFirstResponder:nil];
479 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row;
481 (void)tableView; // Unused
482 NSDictionary *browseDomainDict;
485 if (browseDomainsArray) {
486 browseDomainDict = [browseDomainsArray objectAtIndex:row];
487 if (browseDomainDict) {
488 if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_ENABLED_KEY]) {
489 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
490 } else if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_DOMAIN_KEY]) {
491 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
499 - (void)setupInitialValues
501 [self readPreferences];
503 if (currentHostName) {
504 [hostName setStringValue:currentHostName];
505 [self updateStatusImageView];
508 if (browseDomainsArray) {
509 [browseDomainsArray release];
510 browseDomainsArray = nil;
513 if (currentBrowseDomainsArray) {
514 browseDomainsArray = [currentBrowseDomainsArray mutableCopy];
515 if (browseDomainsArray) {
516 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
517 if ([browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
518 OSStatus err = WriteBrowseDomain((CFDataRef)[self dataForDomainArray:browseDomainsArray]);
519 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", err);
520 [currentBrowseDomainsArray release];
521 currentBrowseDomainsArray = [browseDomainsArray copy];
525 browseDomainsArray = nil;
527 [browseDomainList reloadData];
529 if (currentRegDomain && ([currentRegDomain length] > 0)) {
530 [regDomainsComboBox setStringValue:currentRegDomain];
531 [registrationDataSource removeObject:currentRegDomain];
532 [registrationDataSource addObject:currentRegDomain];
533 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
534 [regDomainsComboBox reloadData];
537 if (currentWideAreaState) {
538 [self toggleWideAreaBonjour:YES];
540 [self toggleWideAreaBonjour:NO];
543 if (hostNameSharedSecretValue) {
544 [hostNameSharedSecretValue release];
545 hostNameSharedSecretValue = nil;
548 if (regSharedSecretValue) {
549 [regSharedSecretValue release];
550 regSharedSecretValue = nil;
553 [self updateApplyButtonState];
554 [mainWindow makeFirstResponder:nil];
555 [browseDomainList deselectAll:self];
556 [removeBrowseDomainButton setEnabled:NO];
565 prefsNeedUpdating = NO;
567 browseDomainListEnabled = NO;
568 defaultRegDomain = nil;
569 currentRegDomain = nil;
570 currentBrowseDomainsArray = nil;
571 currentHostName = nil;
572 hostNameSharedSecretValue = nil;
573 regSharedSecretValue = nil;
574 browseDomainsArray = nil;
575 justStartedEditing = YES;
576 currentWideAreaState = NO;
577 NSString *successPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"success" ofType:@"tiff"];
578 NSString *inprogressPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"inprogress" ofType:@"tiff"];
579 NSString *failurePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"failure" ofType:@"tiff"];
581 registrationDataSource = [[NSMutableArray alloc] init];
582 browseDataSource = [[NSMutableArray alloc] init];
583 defaultBrowseDomainsArray = [[NSMutableArray alloc] init];
584 successImage = [[NSImage alloc] initWithContentsOfFile:successPath];
585 inprogressImage = [[NSImage alloc] initWithContentsOfFile:inprogressPath];
586 failureImage = [[NSImage alloc] initWithContentsOfFile:failurePath];
588 [tabView selectFirstTabViewItem:self];
589 [self setupInitialValues];
590 [self startDomainBrowsing];
591 [self watchForPreferenceChanges];
593 InitConfigAuthority();
594 err = EnsureToolInstalled();
595 if (err == noErr) toolInstalled = YES;
596 else { long int tmp = err; fprintf(stderr, "EnsureToolInstalled returned %ld\n", tmp); }
601 - (IBAction)closeMyCustomSheet:(id)sender
603 BOOL result = [sender isEqualTo:browseOKButton] || [sender isEqualTo:secretOKButton];
605 if (result) [NSApp endSheet:[sender window] returnCode:NSOKButton];
606 else [NSApp endSheet:[sender window] returnCode:NSCancelButton];
610 - (void)sharedSecretSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
612 NSButton * button = (NSButton *)contextInfo;
613 [sheet orderOut:self];
614 [self enableControls];
616 if (returnCode == NSOKButton) {
617 if ([button isEqualTo:hostNameSharedSecretButton]) {
618 hostNameSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
619 hostNameSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
621 regSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
622 regSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
624 [self updateApplyButtonState];
626 [sharedSecretValue setStringValue:@""];
630 - (BOOL)domainAlreadyInList:(NSString *)domainString
632 if (browseDomainsArray) {
633 NSDictionary *domainDict;
634 NSString *domainName;
635 NSEnumerator *arrayEnumerator = [browseDomainsArray objectEnumerator];
636 while ((domainDict = [arrayEnumerator nextObject]) != NULL) {
637 domainName = [domainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
638 if ([domainString caseInsensitiveCompare:domainName] == NSOrderedSame) return YES;
645 - (NSString *)trimCharactersFromDomain:(NSString *)domain
647 NSMutableCharacterSet * trimSet = [[[NSCharacterSet whitespaceCharacterSet] mutableCopy] autorelease];
648 [trimSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
649 return [domain stringByTrimmingCharactersInSet:trimSet];
653 - (void)addBrowseDomainSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
655 (void)contextInfo; // Unused
656 [sheet orderOut:self];
657 [self enableControls];
659 if (returnCode == NSOKButton) {
660 NSString * newBrowseDomainString = [self trimCharactersFromDomain:[browseDomainsComboBox stringValue]];
661 NSMutableDictionary *newBrowseDomainDict;
663 if (browseDomainsArray == nil) browseDomainsArray = [[NSMutableArray alloc] initWithCapacity:0];
664 if ([self domainAlreadyInList:newBrowseDomainString] == NO) {
665 newBrowseDomainDict = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
667 [newBrowseDomainDict setObject:newBrowseDomainString forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
668 [newBrowseDomainDict setObject:[[[NSNumber alloc] initWithBool:YES] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
670 [browseDomainsArray addObject:newBrowseDomainDict];
671 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
672 [browseDomainList reloadData];
673 [self updateApplyButtonState];
679 -(void)validateTextFields
681 [hostName validateEditing];
682 [browseDomainsComboBox validateEditing];
683 [regDomainsComboBox validateEditing];
687 - (IBAction)changeButtonPressed:(id)sender
691 [self disableControls];
692 [self validateTextFields];
693 [mainWindow makeFirstResponder:nil];
694 [browseDomainList deselectAll:sender];
696 if ([sender isEqualTo:hostNameSharedSecretButton]) {
697 if (hostNameSharedSecretValue) {
698 [sharedSecretValue setStringValue:hostNameSharedSecretValue];
699 } else if ((keyName = [self sharedSecretKeyName:[hostName stringValue]]) != NULL) {
700 [sharedSecretName setStringValue:keyName];
701 [sharedSecretValue setStringValue:@"****************"];
703 [sharedSecretName setStringValue:[hostName stringValue]];
704 [sharedSecretValue setStringValue:@""];
708 if (regSharedSecretValue) {
709 [sharedSecretValue setStringValue:regSharedSecretValue];
710 } else if ((keyName = [self sharedSecretKeyName:[regDomainsComboBox stringValue]]) != NULL) {
711 [sharedSecretName setStringValue:keyName];
712 [sharedSecretValue setStringValue:@"****************"];
714 [sharedSecretName setStringValue:[regDomainsComboBox stringValue]];
715 [sharedSecretValue setStringValue:@""];
719 [sharedSecretWindow resignFirstResponder];
721 if ([[sharedSecretName stringValue] length] > 0) [sharedSecretWindow makeFirstResponder:sharedSecretValue];
722 else [sharedSecretWindow makeFirstResponder:sharedSecretName];
724 [NSApp beginSheet:sharedSecretWindow modalForWindow:mainWindow modalDelegate:self
725 didEndSelector:@selector(sharedSecretSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
729 - (IBAction)wideAreaCheckBoxChanged:(id)sender
731 [self toggleWideAreaBonjour:[sender state]];
732 [self updateApplyButtonState];
733 [mainWindow makeFirstResponder:nil];
737 - (void)updateApplyButtonState
739 NSString *hostNameString = [hostName stringValue];
740 NSString *regDomainString = [regDomainsComboBox stringValue];
742 NSComparisonResult hostNameResult = [hostNameString compare:currentHostName];
743 NSComparisonResult regDomainResult = [regDomainString compare:currentRegDomain];
745 if ((currentHostName && (hostNameResult != NSOrderedSame)) ||
746 (currentRegDomain && (regDomainResult != NSOrderedSame) && ([wideAreaCheckBox state])) ||
747 (currentHostName == nil && ([hostNameString length]) > 0) ||
748 (currentRegDomain == nil && ([regDomainString length]) > 0) ||
749 (currentWideAreaState != [wideAreaCheckBox state]) ||
750 (hostNameSharedSecretValue != nil) ||
751 (regSharedSecretValue != nil) ||
752 (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO))
754 [self enableApplyButton];
756 [self disableApplyButton];
762 - (void)controlTextDidChange:(NSNotification *)notification;
764 (void)notification; // Unused
765 [self updateApplyButtonState];
770 - (IBAction)comboAction:(id)sender;
772 (void)sender; // Unused
773 [self updateApplyButtonState];
777 - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)ind
779 NSString *domain = nil;
780 if ([aComboBox isEqualTo:browseDomainsComboBox]) domain = [browseDataSource objectAtIndex:ind];
781 else if ([aComboBox isEqualTo:regDomainsComboBox]) domain = [registrationDataSource objectAtIndex:ind];
787 - (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox
790 if ([aComboBox isEqualTo:browseDomainsComboBox]) count = [browseDataSource count];
791 else if ([aComboBox isEqualTo:regDomainsComboBox]) count = [registrationDataSource count];
796 - (NSMutableArray *)browseDataSource
798 return browseDataSource;
802 - (NSMutableArray *)registrationDataSource
804 return registrationDataSource;
808 - (NSComboBox *)browseDomainsComboBox
810 return browseDomainsComboBox;
814 - (NSComboBox *)regDomainsComboBox
816 return regDomainsComboBox;
820 - (NSString *)currentRegDomain
822 return currentRegDomain;
826 - (NSMutableArray *)defaultBrowseDomainsArray
828 return defaultBrowseDomainsArray;
832 - (NSArray *)currentBrowseDomainsArray
834 return currentBrowseDomainsArray;
838 - (NSString *)currentHostName
840 return currentHostName;
844 - (NSString *)defaultRegDomain
846 return defaultRegDomain;
850 - (void)setDefaultRegDomain:(NSString *)domain
852 [defaultRegDomain release];
853 defaultRegDomain = domain;
854 [defaultRegDomain retain];
861 mainWindow = [[self mainView] window];
865 - (void)mainViewDidLoad
867 [comboAuthButton setString:"system.preferences"];
868 [comboAuthButton setDelegate:self];
869 [comboAuthButton updateStatus:nil];
870 [comboAuthButton setAutoupdate:YES];
875 - (IBAction)applyClicked:(id)sender
877 (void)sender; // Unused
878 [self applyCurrentState];
882 - (void)applyCurrentState
884 [self validateTextFields];
886 if (toolInstalled == YES) {
887 [self savePreferences];
888 [self disableApplyButton];
889 [mainWindow makeFirstResponder:nil];
894 - (void)enableApplyButton
896 [applyButton setEnabled:YES];
897 [revertButton setEnabled:YES];
898 prefsNeedUpdating = YES;
902 - (void)disableApplyButton
904 [applyButton setEnabled:NO];
905 [revertButton setEnabled:NO];
906 prefsNeedUpdating = NO;
910 - (void)toggleWideAreaBonjour:(BOOL)state
912 [wideAreaCheckBox setState:state];
913 [regDomainsComboBox setEnabled:state];
914 [registrationSharedSecretButton setEnabled:state];
918 - (IBAction)revertClicked:(id)sender;
920 [self restorePreferences];
921 [browseDomainList deselectAll:sender];
922 [mainWindow makeFirstResponder:nil];
926 - (void)restorePreferences
928 [self setupInitialValues];
932 - (void)savePanelWillClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
934 (void)sheet; // Unused
935 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)contextInfo;
937 if (returnCode == NSAlertDefaultReturn) {
938 [me applyCurrentState];
939 } else if (returnCode == NSAlertAlternateReturn ) {
940 [me restorePreferences];
944 [me replyToShouldUnselect:(returnCode != NSAlertOtherReturn)];
948 -(SecKeychainItemRef)copyKeychainItemforDomain:(NSString *)domain
950 const char * serviceName = [domain UTF8String];
951 UInt32 type = 'ddns';
952 UInt32 typeLength = sizeof(type);
954 SecKeychainAttribute attrs[] = { { kSecServiceItemAttr, strlen(serviceName), (char *)serviceName },
955 { kSecTypeItemAttr, typeLength, (UInt32 *)&type } };
957 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
958 SecKeychainSearchRef searchRef;
959 SecKeychainItemRef itemRef = NULL;
962 err = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef);
964 err = SecKeychainSearchCopyNext(searchRef, &itemRef);
965 if (err != noErr) itemRef = NULL;
971 -(NSString *)sharedSecretKeyName:(NSString * )domain
973 SecKeychainItemRef itemRef = NULL;
974 NSString *keyName = nil;
977 err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
978 assert(err == noErr);
980 itemRef = [self copyKeychainItemforDomain:[domain lowercaseString]];
983 SecKeychainAttributeInfo attrInfo;
984 SecKeychainAttributeList *attrList = NULL;
985 SecKeychainAttribute attribute;
988 tags[0] = kSecAccountItemAttr;
991 attrInfo.format = NULL;
993 err = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, NULL, NULL);
995 for (i = 0; i < attrList->count; i++) {
996 attribute = attrList->attr[i];
997 if (attribute.tag == kSecAccountItemAttr) {
998 keyName = [[NSString alloc] initWithBytes:attribute.data length:attribute.length encoding:NSUTF8StringEncoding];
1002 if (attrList) (void)SecKeychainItemFreeAttributesAndData(attrList, NULL);
1010 -(NSString *)domainForHostName:(NSString *)hostNameString
1012 NSString * domainName = nil;
1016 ptr = (char *)[hostNameString UTF8String];
1018 ptr = (char *)GetNextLabel(ptr, text);
1019 domainName = [[NSString alloc] initWithUTF8String:(const char *)ptr];
1021 return ([domainName autorelease]);
1025 - (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled
1027 NSMutableArray *domainsArray;
1028 NSMutableDictionary *domainDict = nil;
1030 if (domainName && [domainName length] > 0) {
1031 domainDict= [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
1032 [domainDict setObject:domainName forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
1033 [domainDict setObject:[[[NSNumber alloc] initWithBool:enabled] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
1035 domainsArray = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
1036 if (domainDict) [domainsArray addObject:domainDict];
1037 return [NSArchiver archivedDataWithRootObject:domainsArray];
1041 - (NSData *)dataForDomainArray:(NSArray *)domainArray
1043 return [NSArchiver archivedDataWithRootObject:domainArray];
1047 - (NSData *)dataForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName
1049 NSMutableDictionary *sharedSecretDict = [[[NSMutableDictionary alloc] initWithCapacity:3] autorelease];
1050 [sharedSecretDict setObject:secret forKey:(NSString *)SC_DYNDNS_SECRET_KEY];
1051 [sharedSecretDict setObject:[domainName lowercaseString] forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
1052 [sharedSecretDict setObject:keyName forKey:(NSString *)SC_DYNDNS_KEYNAME_KEY];
1053 return [NSArchiver archivedDataWithRootObject:sharedSecretDict];
1057 -(void)savePreferences
1059 NSString *hostNameString = [hostName stringValue];
1060 NSString *browseDomainString = [browseDomainsComboBox stringValue];
1061 NSString *regDomainString = [regDomainsComboBox stringValue];
1062 NSString *tempHostNameSharedSecretName = hostNameSharedSecretName;
1063 NSString *tempRegSharedSecretName = regSharedSecretName;
1064 NSData *browseDomainData = nil;
1065 BOOL regSecretWasSet = NO;
1066 BOOL hostSecretWasSet = NO;
1067 OSStatus err = noErr;
1069 hostNameString = [self trimCharactersFromDomain:hostNameString];
1070 browseDomainString = [self trimCharactersFromDomain:browseDomainString];
1071 regDomainString = [self trimCharactersFromDomain:regDomainString];
1072 tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName];
1073 tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName];
1075 [hostName setStringValue:hostNameString];
1076 [regDomainsComboBox setStringValue:regDomainString];
1078 // Convert Shared Secret account names to lowercase.
1079 tempHostNameSharedSecretName = [tempHostNameSharedSecretName lowercaseString];
1080 tempRegSharedSecretName = [tempRegSharedSecretName lowercaseString];
1082 // Save hostname shared secret.
1083 if ([hostNameSharedSecretName length] > 0 && ([hostNameSharedSecretValue length] > 0)) {
1084 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:hostNameSharedSecretValue domain:hostNameString key:tempHostNameSharedSecretName]);
1085 [hostNameSharedSecretValue release];
1086 hostNameSharedSecretValue = nil;
1087 hostSecretWasSet = YES;
1090 // Save registration domain shared secret.
1091 if (([regSharedSecretName length] > 0) && ([regSharedSecretValue length] > 0)) {
1092 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:regSharedSecretValue domain:regDomainString key:tempRegSharedSecretName]);
1093 [regSharedSecretValue release];
1094 regSharedSecretValue = nil;
1095 regSecretWasSet = YES;
1099 if ((currentHostName == NULL) || [currentHostName compare:hostNameString] != NSOrderedSame) {
1100 err = WriteHostname((CFDataRef)[self dataForDomain:hostNameString isEnabled:YES]);
1101 if (err != noErr) NSLog(@"WriteHostname returned %d\n", err);
1102 currentHostName = [hostNameString copy];
1103 } else if (hostSecretWasSet) {
1104 WriteHostname((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1105 usleep(200000); // Temporary hack
1106 if ([currentHostName length] > 0) WriteHostname((CFDataRef)[self dataForDomain:(NSString *)currentHostName isEnabled:YES]);
1109 // Save browse domain.
1110 if (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
1111 browseDomainData = [self dataForDomainArray:browseDomainsArray];
1112 err = WriteBrowseDomain((CFDataRef)browseDomainData);
1113 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", err);
1114 currentBrowseDomainsArray = [browseDomainsArray copy];
1117 // Save registration domain.
1118 if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) {
1120 err = WriteRegistrationDomain((CFDataRef)[self dataForDomain:regDomainString isEnabled:[wideAreaCheckBox state]]);
1121 if (err != noErr) NSLog(@"WriteRegistrationDomain returned %d\n", err);
1123 if (currentRegDomain) CFRelease(currentRegDomain);
1124 currentRegDomain = [regDomainString copy];
1126 if ([currentRegDomain length] > 0) {
1127 currentWideAreaState = [wideAreaCheckBox state];
1128 [registrationDataSource removeObject:regDomainString];
1129 [registrationDataSource addObject:currentRegDomain];
1130 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
1131 [regDomainsComboBox reloadData];
1133 currentWideAreaState = NO;
1134 [self toggleWideAreaBonjour:NO];
1135 if (defaultRegDomain != nil) [regDomainsComboBox setStringValue:defaultRegDomain];
1137 } else if (regSecretWasSet) {
1138 WriteRegistrationDomain((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1139 usleep(200000); // Temporary hack
1140 if ([currentRegDomain length] > 0) WriteRegistrationDomain((CFDataRef)[self dataForDomain:currentRegDomain isEnabled:currentWideAreaState]);
1145 - (NSPreferencePaneUnselectReply)shouldUnselect
1148 if (prefsNeedUpdating == YES) {
1150 [self disableControls];
1153 @"Apply Configuration Changes?",
1159 @selector( savePanelWillClose:returnCode:contextInfo: ),
1161 (void *) self, // sender,
1163 return NSUnselectLater;
1167 return NSUnselectNow;
1171 -(void)disableControls
1173 [hostName setEnabled:NO];
1174 [hostNameSharedSecretButton setEnabled:NO];
1175 [browseDomainsComboBox setEnabled:NO];
1176 [applyButton setEnabled:NO];
1177 [revertButton setEnabled:NO];
1178 [wideAreaCheckBox setEnabled:NO];
1179 [regDomainsComboBox setEnabled:NO];
1180 [registrationSharedSecretButton setEnabled:NO];
1181 [statusImageView setEnabled:NO];
1183 browseDomainListEnabled = NO;
1184 [browseDomainList deselectAll:self];
1185 [browseDomainList setEnabled:NO];
1187 [addBrowseDomainButton setEnabled:NO];
1188 [removeBrowseDomainButton setEnabled:NO];
1192 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row;
1194 (void)row; // Unused
1195 (void)tableView; // Unused
1196 return browseDomainListEnabled;
1200 -(void)enableControls
1202 [hostName setEnabled:YES];
1203 [hostNameSharedSecretButton setEnabled:YES];
1204 [browseDomainsComboBox setEnabled:YES];
1205 [wideAreaCheckBox setEnabled:YES];
1206 [registrationSharedSecretButton setEnabled:YES];
1207 [self toggleWideAreaBonjour:[wideAreaCheckBox state]];
1208 [statusImageView setEnabled:YES];
1209 [addBrowseDomainButton setEnabled:YES];
1211 [browseDomainList setEnabled:YES];
1212 [browseDomainList deselectAll:self];
1213 browseDomainListEnabled = YES;
1215 [removeBrowseDomainButton setEnabled:[browseDomainList numberOfSelectedRows]];
1216 [applyButton setEnabled:prefsNeedUpdating];
1217 [revertButton setEnabled:prefsNeedUpdating];
1221 - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view
1223 (void)view; // Unused
1224 [self enableControls];
1228 - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view
1230 (void)view; // Unused
1231 [self disableControls];
1237 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
1238 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
1239 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
1240 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
1241 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
1243 // NOT static -- otherwise the compiler may optimize it out
1244 // The "@(#) " pattern is a special prefix the "what" command looks for
1245 const char VersionString_SCCS[] = "@(#) Bonjour Preference Pane " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
1247 #if _BUILDING_XCODE_PROJECT_
1248 // If the process crashes, then this string will be magically included in the automatically-generated crash log
1249 const char *__crashreporter_info__ = VersionString_SCCS + 5;
1250 asm(".desc ___crashreporter_info__, 0x10");