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.8 2006/08/14 23:15:47 cheshire
47 Tidy up Change History comment
49 Revision 1.7 2006/07/14 03:59:14 cheshire
50 Fix compile warnings: 'sortUsingFunction:context:' comparison function needs to return int
52 Revision 1.6 2005/02/26 00:44:24 cheshire
53 Restore default reg domain if user deletes text and clicks "apply"
55 Revision 1.5 2005/02/25 02:29:28 cheshire
56 Show yellow dot for "update in progress"
58 Revision 1.4 2005/02/16 00:18:33 cheshire
61 Revision 1.3 2005/02/10 22:35:20 cheshire
62 <rdar://problem/3727944> Update name
64 Revision 1.2 2005/02/08 01:32:05 cheshire
65 Add trimCharactersFromDomain routine to strip leading and trailing
66 white space and punctuation from user-entered fields.
68 Revision 1.1 2005/02/05 01:59:19 cheshire
69 Add Preference Pane to facilitate testing of DDNS & wide-area features
73 #import "DNSServiceDiscoveryPref.h"
74 #import "ConfigurationAuthority.h"
75 #import "PrivilegedOperations.h"
78 @implementation DNSServiceDiscoveryPref
81 MyArrayCompareFunction(id val1, id val2, void *context)
83 return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive);
88 MyDomainArrayCompareFunction(id val1, id val2, void *context)
90 NSString *domain1 = [val1 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
91 NSString *domain2 = [val2 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
92 return CFStringCompare((CFStringRef)domain1, (CFStringRef)domain2, kCFCompareCaseInsensitive);
97 GetNextLabel(const char *cstr, char label[64])
100 while (*cstr && *cstr != '.') // While we have characters in the label...
106 if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1]))
108 int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal
109 int v1 = cstr[ 0] - '0';
110 int v2 = cstr[ 1] - '0';
111 int val = v0 * 100 + v1 * 10 + v2;
112 if (val <= 255) { c = (char)val; cstr += 2; } // If valid three-digit decimal value, use it
116 if (ptr >= label+64) return(NULL);
118 if (*cstr) cstr++; // Skip over the trailing dot (if present)
124 static void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
126 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
129 [me setupInitialValues];
133 static void ServiceDomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
134 DNSServiceErrorType errorCode, const char *replyDomain, void *context, DNSServiceFlags enumType)
136 if (strcmp(replyDomain, "local.") == 0) return; // local domain is not interesting
138 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
139 BOOL moreComing = (BOOL)(flags & kDNSServiceFlagsMoreComing);
140 NSMutableArray * domainArray;
141 NSMutableArray * defaultBrowseDomainsArray = nil;
142 NSComboBox * domainComboBox;
143 NSString * domainString;
144 NSString * currentDomain = nil;
145 char decodedDomainString[kDNSServiceMaxDomainName] = "\0";
146 char nextLabel[256] = "\0";
147 char * buffer = (char *)replyDomain;
150 buffer = (char *)GetNextLabel(buffer, nextLabel);
151 strcat(decodedDomainString, nextLabel);
152 strcat(decodedDomainString, ".");
155 // Remove trailing dot from domain name.
156 decodedDomainString[strlen(decodedDomainString)-1] = '\0';
158 domainString = [[[NSString alloc] initWithUTF8String:(const char *)decodedDomainString] autorelease];
160 if (enumType & kDNSServiceFlagsRegistrationDomains) {
161 domainArray = [me registrationDataSource];
162 domainComboBox = [me regDomainsComboBox];
163 currentDomain = [me currentRegDomain];
165 domainArray = [me browseDataSource];
166 domainComboBox = [me browseDomainsComboBox];
167 defaultBrowseDomainsArray = [me defaultBrowseDomainsArray];
170 if (flags & kDNSServiceFlagsAdd) {
171 [domainArray removeObject:domainString]; // How can I check if an object is in the array?
172 [domainArray addObject:domainString];
173 if ((flags & kDNSServiceFlagsDefault) && (enumType & kDNSServiceFlagsRegistrationDomains)) {
174 [me setDefaultRegDomain:domainString];
175 if ([[domainComboBox stringValue] length] == 0) [domainComboBox setStringValue:domainString];
176 } else if ((flags & kDNSServiceFlagsDefault) && !(enumType & kDNSServiceFlagsRegistrationDomains)) {
177 [defaultBrowseDomainsArray removeObject:domainString];
178 [defaultBrowseDomainsArray addObject:domainString];
182 if (moreComing == NO) {
183 [domainArray sortUsingFunction:MyArrayCompareFunction context:nil];
184 [domainComboBox reloadData];
190 browseDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
191 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
193 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsBrowseDomains);
198 registrationDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
199 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
201 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsRegistrationDomains);
207 MyDNSServiceCleanUp(MyDNSServiceState * query)
209 /* Remove the CFRunLoopSource from the current run loop. */
210 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
211 CFRelease(query->source);
213 /* Invalidate the CFSocket. */
214 CFSocketInvalidate(query->socket);
215 CFRelease(query->socket);
217 /* Workaround that gives time to CFSocket's select thread so it can remove the socket from its FD set
218 before we close the socket by calling DNSServiceRefDeallocate. <rdar://problem/3585273> */
221 /* Terminate the connection with the mDNSResponder daemon, which cancels the query. */
222 DNSServiceRefDeallocate(query->service);
228 MySocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void * data, void * info)
232 #pragma unused(address)
235 DNSServiceErrorType err;
237 MyDNSServiceState * query = (MyDNSServiceState *)info; // context passed in to CFSocketCreateWithNative().
238 assert(query != NULL);
240 /* Read a reply from the mDNSResponder. */
241 err= DNSServiceProcessResult(query->service);
242 if (err != kDNSServiceErr_NoError) {
243 fprintf(stderr, "DNSServiceProcessResult returned %d\n", err);
245 /* Terminate the query operation and release the CFRunLoopSource and CFSocket. */
246 MyDNSServiceCleanUp(query);
253 MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query)
255 CFSocketNativeHandle sock;
256 CFOptionFlags sockFlags;
257 CFSocketContext context = { 0, query, NULL, NULL, NULL }; // Use MyDNSServiceState as context data.
259 /* Access the underlying Unix domain socket to communicate with the mDNSResponder daemon. */
260 sock = DNSServiceRefSockFD(query->service);
263 /* Create a CFSocket using the Unix domain socket. */
264 query->socket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, MySocketReadCallback, &context);
265 assert(query->socket != NULL);
267 /* Prevent CFSocketInvalidate from closing DNSServiceRef's socket. */
268 sockFlags = CFSocketGetSocketFlags(query->socket);
269 CFSocketSetSocketFlags(query->socket, sockFlags & (~kCFSocketCloseOnInvalidate));
271 /* Create a CFRunLoopSource from the CFSocket. */
272 query->source = CFSocketCreateRunLoopSource(NULL, query->socket, 0);
273 assert(query->source != NULL);
275 /* Add the CFRunLoopSource to the current run loop. */
276 CFRunLoopAddSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
281 -(void)updateStatusImageView
283 int value = [self statusForHostName:currentHostName];
284 if (value == 0) [statusImageView setImage:successImage];
285 else if (value > 0) [statusImageView setImage:inprogressImage];
286 else [statusImageView setImage:failureImage];
290 - (void)watchForPreferenceChanges
292 SCDynamicStoreContext context = { 0, self, NULL, NULL, NULL };
293 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("watchForPreferenceChanges"), NetworkChanged, &context);
294 CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
295 CFRunLoopSourceRef rls;
297 assert(store != NULL);
298 assert(keys != NULL);
300 CFArrayAppendValue(keys, SC_DYNDNS_STATE_KEY);
301 CFArrayAppendValue(keys, SC_DYNDNS_SETUP_KEY);
303 (void)SCDynamicStoreSetNotificationKeys(store, keys, NULL);
305 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
308 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
315 -(int)statusForHostName:(NSString * )domain
317 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("statusForHostName"), NULL, NULL);
318 NSString *lowercaseDomain = [domain lowercaseString];
321 assert(store != NULL);
323 NSDictionary *dynamicDNS = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY);
325 NSDictionary *hostNames = [dynamicDNS objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
326 NSDictionary *infoDict = [hostNames objectForKey:lowercaseDomain];
327 if (infoDict) status = [[infoDict objectForKey:(NSString*)SC_DYNDNS_STATUS_KEY] intValue];
328 CFRelease(dynamicDNS);
336 - (void)startDomainBrowsing
338 DNSServiceFlags flags;
339 OSStatus err = noErr;
341 flags = kDNSServiceFlagsRegistrationDomains;
342 err = DNSServiceEnumerateDomains(®Query.service, flags, 0, registrationDomainReply, (void *)self);
343 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(®Query);
345 flags = kDNSServiceFlagsBrowseDomains;
346 err = DNSServiceEnumerateDomains(&browseQuery.service, flags, 0, browseDomainReply, (void *)self);
347 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(&browseQuery);
351 -(void)readPreferences
353 NSDictionary *origDict;
354 NSArray *regDomainArray;
357 if (currentRegDomain) [currentRegDomain release];
358 if (currentBrowseDomainsArray) [currentBrowseDomainsArray release];
359 if (currentHostName) [currentHostName release];
361 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL);
362 origDict = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY);
364 regDomainArray = [origDict objectForKey:(NSString *)SC_DYNDNS_REGDOMAINS_KEY];
365 if (regDomainArray && [regDomainArray count] > 0) {
366 currentRegDomain = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
367 currentWideAreaState = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
369 currentRegDomain = [[NSString alloc] initWithString:@""];
370 currentWideAreaState = NO;
373 currentBrowseDomainsArray = [[origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY] retain];
375 hostArray = [origDict objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
376 if (hostArray && [hostArray count] > 0) {
377 currentHostName = [[[hostArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
379 currentHostName = [[NSString alloc] initWithString:@""];
387 - (void)tableViewSelectionDidChange:(NSNotification *)notification;
389 [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]];
393 - (void)setBrowseDomainsComboBox;
395 NSString * domain = nil;
397 if ([defaultBrowseDomainsArray count] > 0) {
398 NSEnumerator * arrayEnumerator = [defaultBrowseDomainsArray objectEnumerator];
399 while (domain = [arrayEnumerator nextObject]) {
400 if ([self domainAlreadyInList:domain] == NO) break;
403 if (domain) [browseDomainsComboBox setStringValue:domain];
404 else [browseDomainsComboBox setStringValue:@""];
408 - (IBAction)addBrowseDomainClicked:(id)sender;
410 [self setBrowseDomainsComboBox];
412 [NSApp beginSheet:addBrowseDomainWindow modalForWindow:mainWindow modalDelegate:self
413 didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
415 [browseDomainList deselectAll:sender];
416 [self updateApplyButtonState];
420 - (IBAction)removeBrowseDomainClicked:(id)sender;
422 int selectedBrowseDomain = [browseDomainList selectedRow];
423 [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain];
424 [browseDomainList reloadData];
425 [self updateApplyButtonState];
429 - (IBAction)enableBrowseDomainClicked:(id)sender;
431 NSTableView *tableView = sender;
432 NSMutableDictionary *browseDomainDict;
435 browseDomainDict = [[browseDomainsArray objectAtIndex:[tableView clickedRow]] mutableCopy];
436 value = [[browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
437 [browseDomainDict setObject:[[[NSNumber alloc] initWithInt:(!value)] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
438 [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict];
439 [tableView reloadData];
440 [self updateApplyButtonState];
445 - (int)numberOfRowsInTableView:(NSTableView *)tableView;
447 int numberOfRows = 0;
449 if (browseDomainsArray) {
450 numberOfRows = [browseDomainsArray count];
456 - (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem;
458 [browseDomainList deselectAll:self];
459 [mainWindow makeFirstResponder:nil];
463 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row;
465 NSDictionary *browseDomainDict;
468 if (browseDomainsArray) {
469 browseDomainDict = [browseDomainsArray objectAtIndex:row];
470 if (browseDomainDict) {
471 if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_ENABLED_KEY]) {
472 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
473 } else if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_DOMAIN_KEY]) {
474 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
482 - (void)setupInitialValues
484 [self readPreferences];
486 if (currentHostName) {
487 [hostName setStringValue:currentHostName];
488 [self updateStatusImageView];
491 if (browseDomainsArray) {
492 [browseDomainsArray release];
493 browseDomainsArray = nil;
496 if (currentBrowseDomainsArray) {
497 browseDomainsArray = [currentBrowseDomainsArray mutableCopy];
498 if (browseDomainsArray) {
499 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
500 if ([browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
501 OSStatus err = WriteBrowseDomain((CFDataRef)[self dataForDomainArray:browseDomainsArray]);
502 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", err);
503 [currentBrowseDomainsArray release];
504 currentBrowseDomainsArray = [browseDomainsArray copy];
508 browseDomainsArray = nil;
510 [browseDomainList reloadData];
512 if (currentRegDomain && ([currentRegDomain length] > 0)) {
513 [regDomainsComboBox setStringValue:currentRegDomain];
514 [registrationDataSource removeObject:currentRegDomain];
515 [registrationDataSource addObject:currentRegDomain];
516 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
517 [regDomainsComboBox reloadData];
520 if (currentWideAreaState) {
521 [self toggleWideAreaBonjour:YES];
523 [self toggleWideAreaBonjour:NO];
526 if (hostNameSharedSecretValue) {
527 [hostNameSharedSecretValue release];
528 hostNameSharedSecretValue = nil;
531 if (regSharedSecretValue) {
532 [regSharedSecretValue release];
533 regSharedSecretValue = nil;
536 [self updateApplyButtonState];
537 [mainWindow makeFirstResponder:nil];
538 [browseDomainList deselectAll:self];
539 [removeBrowseDomainButton setEnabled:NO];
548 prefsNeedUpdating = NO;
550 browseDomainListEnabled = NO;
551 defaultRegDomain = nil;
552 currentRegDomain = nil;
553 currentBrowseDomainsArray = nil;
554 currentHostName = nil;
555 hostNameSharedSecretValue = nil;
556 regSharedSecretValue = nil;
557 browseDomainsArray = nil;
558 justStartedEditing = YES;
559 currentWideAreaState = NO;
560 NSString *successPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"success" ofType:@"tiff"];
561 NSString *inprogressPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"inprogress" ofType:@"tiff"];
562 NSString *failurePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"failure" ofType:@"tiff"];
564 registrationDataSource = [[NSMutableArray alloc] init];
565 browseDataSource = [[NSMutableArray alloc] init];
566 defaultBrowseDomainsArray = [[NSMutableArray alloc] init];
567 successImage = [[NSImage alloc] initWithContentsOfFile:successPath];
568 inprogressImage = [[NSImage alloc] initWithContentsOfFile:inprogressPath];
569 failureImage = [[NSImage alloc] initWithContentsOfFile:failurePath];
571 [tabView selectFirstTabViewItem:self];
572 [self setupInitialValues];
573 [self startDomainBrowsing];
574 [self watchForPreferenceChanges];
576 [tabView setDelegate:self];
578 InitConfigAuthority();
579 err = EnsureToolInstalled();
580 if (err == noErr) toolInstalled = YES;
581 else fprintf(stderr, "EnsureToolInstalled returned %ld\n", err);
586 - (IBAction)closeMyCustomSheet:(id)sender
588 BOOL result = [sender isEqualTo:browseOKButton] || [sender isEqualTo:secretOKButton];
590 if (result) [NSApp endSheet:[sender window] returnCode:NSOKButton];
591 else [NSApp endSheet:[sender window] returnCode:NSCancelButton];
595 - (void)sharedSecretSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
597 NSButton * button = (NSButton *)contextInfo;
598 [sheet orderOut:self];
599 [self enableControls];
601 if (returnCode == NSOKButton) {
602 if ([button isEqualTo:hostNameSharedSecretButton]) {
603 hostNameSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
604 hostNameSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
606 regSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
607 regSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
609 [self updateApplyButtonState];
611 [sharedSecretValue setStringValue:@""];
615 - (BOOL)domainAlreadyInList:(NSString *)domainString
617 if (browseDomainsArray) {
618 NSDictionary *domainDict;
619 NSString *domainName;
620 NSEnumerator *arrayEnumerator = [browseDomainsArray objectEnumerator];
621 while (domainDict = [arrayEnumerator nextObject]) {
622 domainName = [domainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
623 if ([domainString caseInsensitiveCompare:domainName] == NSOrderedSame) return YES;
630 - (NSString *)trimCharactersFromDomain:(NSString *)domain
632 NSMutableCharacterSet * trimSet = [[[NSCharacterSet whitespaceCharacterSet] mutableCopy] autorelease];
633 [trimSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
634 return [domain stringByTrimmingCharactersInSet:trimSet];
638 - (void)addBrowseDomainSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
640 [sheet orderOut:self];
641 [self enableControls];
643 if (returnCode == NSOKButton) {
644 NSString * newBrowseDomainString = [self trimCharactersFromDomain:[browseDomainsComboBox stringValue]];
645 NSMutableDictionary *newBrowseDomainDict;
647 if (browseDomainsArray == nil) browseDomainsArray = [[NSMutableArray alloc] initWithCapacity:0];
648 if ([self domainAlreadyInList:newBrowseDomainString] == NO) {
649 newBrowseDomainDict = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
651 [newBrowseDomainDict setObject:newBrowseDomainString forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
652 [newBrowseDomainDict setObject:[[[NSNumber alloc] initWithBool:YES] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
654 [browseDomainsArray addObject:newBrowseDomainDict];
655 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
656 [browseDomainList reloadData];
657 [self updateApplyButtonState];
663 -(void)validateTextFields
665 [hostName validateEditing];
666 [browseDomainsComboBox validateEditing];
667 [regDomainsComboBox validateEditing];
671 - (IBAction)changeButtonPressed:(id)sender
675 [self disableControls];
676 [self validateTextFields];
677 [mainWindow makeFirstResponder:nil];
678 [browseDomainList deselectAll:sender];
680 if ([sender isEqualTo:hostNameSharedSecretButton]) {
681 if (hostNameSharedSecretValue) {
682 [sharedSecretValue setStringValue:hostNameSharedSecretValue];
683 } else if (keyName = [self sharedSecretKeyName:[hostName stringValue]]) {
684 [sharedSecretName setStringValue:keyName];
685 [sharedSecretValue setStringValue:@"****************"];
687 [sharedSecretName setStringValue:[hostName stringValue]];
688 [sharedSecretValue setStringValue:@""];
692 if (regSharedSecretValue) {
693 [sharedSecretValue setStringValue:regSharedSecretValue];
694 } else if (keyName = [self sharedSecretKeyName:[regDomainsComboBox stringValue]]) {
695 [sharedSecretName setStringValue:keyName];
696 [sharedSecretValue setStringValue:@"****************"];
698 [sharedSecretName setStringValue:[regDomainsComboBox stringValue]];
699 [sharedSecretValue setStringValue:@""];
703 [sharedSecretWindow resignFirstResponder];
705 if ([[sharedSecretName stringValue] length] > 0) [sharedSecretWindow makeFirstResponder:sharedSecretValue];
706 else [sharedSecretWindow makeFirstResponder:sharedSecretName];
708 [NSApp beginSheet:sharedSecretWindow modalForWindow:mainWindow modalDelegate:self
709 didEndSelector:@selector(sharedSecretSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
713 - (IBAction)wideAreaCheckBoxChanged:(id)sender
715 [self toggleWideAreaBonjour:[sender state]];
716 [self updateApplyButtonState];
717 [mainWindow makeFirstResponder:nil];
721 - (void)updateApplyButtonState
723 NSString *hostNameString = [hostName stringValue];
724 NSString *regDomainString = [regDomainsComboBox stringValue];
726 NSComparisonResult hostNameResult = [hostNameString compare:currentHostName];
727 NSComparisonResult regDomainResult = [regDomainString compare:currentRegDomain];
729 if ((currentHostName && (hostNameResult != NSOrderedSame)) ||
730 (currentRegDomain && (regDomainResult != NSOrderedSame) && ([wideAreaCheckBox state])) ||
731 (currentHostName == nil && ([hostNameString length]) > 0) ||
732 (currentRegDomain == nil && ([regDomainString length]) > 0) ||
733 (currentWideAreaState != [wideAreaCheckBox state]) ||
734 (hostNameSharedSecretValue != nil) ||
735 (regSharedSecretValue != nil) ||
736 (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO))
738 [self enableApplyButton];
740 [self disableApplyButton];
746 - (void)controlTextDidChange:(NSNotification *)notification;
748 [self updateApplyButtonState];
753 - (IBAction)comboAction:(id)sender;
755 [self updateApplyButtonState];
759 - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)index
761 NSString *domain = nil;
762 if ([aComboBox isEqualTo:browseDomainsComboBox]) domain = [browseDataSource objectAtIndex:index];
763 else if ([aComboBox isEqualTo:regDomainsComboBox]) domain = [registrationDataSource objectAtIndex:index];
769 - (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox
772 if ([aComboBox isEqualTo:browseDomainsComboBox]) count = [browseDataSource count];
773 else if ([aComboBox isEqualTo:regDomainsComboBox]) count = [registrationDataSource count];
778 - (NSMutableArray *)browseDataSource
780 return browseDataSource;
784 - (NSMutableArray *)registrationDataSource
786 return registrationDataSource;
790 - (NSComboBox *)browseDomainsComboBox
792 return browseDomainsComboBox;
796 - (NSComboBox *)regDomainsComboBox
798 return regDomainsComboBox;
802 - (NSString *)currentRegDomain
804 return currentRegDomain;
808 - (NSMutableArray *)defaultBrowseDomainsArray
810 return defaultBrowseDomainsArray;
814 - (NSArray *)currentBrowseDomainsArray
816 return currentBrowseDomainsArray;
820 - (NSString *)currentHostName
822 return currentHostName;
826 - (NSString *)defaultRegDomain
828 return defaultRegDomain;
832 - (void)setDefaultRegDomain:(NSString *)domain
834 [defaultRegDomain release];
835 defaultRegDomain = domain;
836 [defaultRegDomain retain];
843 mainWindow = [[self mainView] window];
847 - (void)mainViewDidLoad
849 [comboAuthButton setString:"system.preferences"];
850 [comboAuthButton setDelegate:self];
851 [comboAuthButton updateStatus:nil];
852 [comboAuthButton setAutoupdate:YES];
857 - (IBAction)applyClicked:(id)sender
859 [self applyCurrentState];
863 - (void)applyCurrentState
865 [self validateTextFields];
867 if (toolInstalled == YES) {
868 [self savePreferences];
869 [self disableApplyButton];
870 [mainWindow makeFirstResponder:nil];
875 - (void)enableApplyButton
877 [applyButton setEnabled:YES];
878 [revertButton setEnabled:YES];
879 prefsNeedUpdating = YES;
883 - (void)disableApplyButton
885 [applyButton setEnabled:NO];
886 [revertButton setEnabled:NO];
887 prefsNeedUpdating = NO;
891 - (void)toggleWideAreaBonjour:(BOOL)state
893 [wideAreaCheckBox setState:state];
894 [regDomainsComboBox setEnabled:state];
895 [registrationSharedSecretButton setEnabled:state];
899 - (IBAction)revertClicked:(id)sender;
901 [self restorePreferences];
902 [browseDomainList deselectAll:sender];
903 [mainWindow makeFirstResponder:nil];
907 - (void)restorePreferences
909 [self setupInitialValues];
913 - (void)savePanelWillClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
915 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)contextInfo;
917 if (returnCode == NSAlertDefaultReturn) {
918 [me applyCurrentState];
919 } else if (returnCode == NSAlertAlternateReturn ) {
920 [me restorePreferences];
924 [me replyToShouldUnselect:(returnCode != NSAlertOtherReturn)];
928 -(SecKeychainItemRef)copyKeychainItemforDomain:(NSString *)domain
930 const char * serviceName = [domain UTF8String];
931 UInt32 type = 'ddns';
932 UInt32 typeLength = sizeof(type);
934 SecKeychainAttribute attrs[] = { { kSecServiceItemAttr, strlen(serviceName), (char *)serviceName },
935 { kSecTypeItemAttr, typeLength, (UInt32 *)&type } };
937 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
938 SecKeychainSearchRef searchRef;
939 SecKeychainItemRef itemRef = NULL;
942 err = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef);
944 err = SecKeychainSearchCopyNext(searchRef, &itemRef);
945 if (err != noErr) itemRef = NULL;
951 -(NSString *)sharedSecretKeyName:(NSString * )domain
953 SecKeychainItemRef itemRef = NULL;
954 NSString *keyName = nil;
957 err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
958 assert(err == noErr);
960 itemRef = [self copyKeychainItemforDomain:[domain lowercaseString]];
963 SecKeychainAttributeInfo attrInfo;
964 SecKeychainAttributeList *attrList = NULL;
965 SecKeychainAttribute attribute;
968 tags[0] = kSecAccountItemAttr;
971 attrInfo.format = NULL;
973 err = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, NULL, NULL);
975 for (i = 0; i < attrList->count; i++) {
976 attribute = attrList->attr[i];
977 if (attribute.tag == kSecAccountItemAttr) {
978 keyName = [[NSString alloc] initWithBytes:attribute.data length:attribute.length encoding:NSUTF8StringEncoding];
982 if (attrList) (void)SecKeychainItemFreeAttributesAndData(attrList, NULL);
990 -(NSString *)domainForHostName:(NSString *)hostNameString
992 NSString * domainName = nil;
996 ptr = (char *)[hostNameString UTF8String];
998 ptr = (char *)GetNextLabel(ptr, text);
999 domainName = [[NSString alloc] initWithUTF8String:(const char *)ptr];
1001 return ([domainName autorelease]);
1005 - (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled
1007 NSMutableArray *domainsArray;
1008 NSMutableDictionary *domainDict = nil;
1010 if (domainName && [domainName length] > 0) {
1011 domainDict= [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
1012 [domainDict setObject:domainName forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
1013 [domainDict setObject:[[[NSNumber alloc] initWithBool:enabled] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
1015 domainsArray = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
1016 if (domainDict) [domainsArray addObject:domainDict];
1017 return [NSArchiver archivedDataWithRootObject:domainsArray];
1021 - (NSData *)dataForDomainArray:(NSArray *)domainArray
1023 return [NSArchiver archivedDataWithRootObject:domainArray];
1027 - (NSData *)dataForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName
1029 NSMutableDictionary *sharedSecretDict = [[[NSMutableDictionary alloc] initWithCapacity:3] autorelease];
1030 [sharedSecretDict setObject:secret forKey:(NSString *)SC_DYNDNS_SECRET_KEY];
1031 [sharedSecretDict setObject:[domainName lowercaseString] forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
1032 [sharedSecretDict setObject:keyName forKey:(NSString *)SC_DYNDNS_KEYNAME_KEY];
1033 return [NSArchiver archivedDataWithRootObject:sharedSecretDict];
1037 -(void)savePreferences
1039 NSString *hostNameString = [hostName stringValue];
1040 NSString *browseDomainString = [browseDomainsComboBox stringValue];
1041 NSString *regDomainString = [regDomainsComboBox stringValue];
1042 NSString *tempHostNameSharedSecretName = hostNameSharedSecretName;
1043 NSString *tempRegSharedSecretName = regSharedSecretName;
1044 NSData *browseDomainData = nil;
1045 BOOL regSecretWasSet = NO;
1046 BOOL hostSecretWasSet = NO;
1047 OSStatus err = noErr;
1049 hostNameString = [self trimCharactersFromDomain:hostNameString];
1050 browseDomainString = [self trimCharactersFromDomain:browseDomainString];
1051 regDomainString = [self trimCharactersFromDomain:regDomainString];
1052 tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName];
1053 tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName];
1055 [hostName setStringValue:hostNameString];
1056 [regDomainsComboBox setStringValue:regDomainString];
1058 // Convert Shared Secret account names to lowercase.
1059 tempHostNameSharedSecretName = [tempHostNameSharedSecretName lowercaseString];
1060 tempRegSharedSecretName = [tempRegSharedSecretName lowercaseString];
1062 // Save hostname shared secret.
1063 if ([hostNameSharedSecretName length] > 0 && ([hostNameSharedSecretValue length] > 0)) {
1064 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:hostNameSharedSecretValue domain:hostNameString key:tempHostNameSharedSecretName]);
1065 [hostNameSharedSecretValue release];
1066 hostNameSharedSecretValue = nil;
1067 hostSecretWasSet = YES;
1070 // Save registration domain shared secret.
1071 if (([regSharedSecretName length] > 0) && ([regSharedSecretValue length] > 0)) {
1072 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:regSharedSecretValue domain:regDomainString key:tempRegSharedSecretName]);
1073 [regSharedSecretValue release];
1074 regSharedSecretValue = nil;
1075 regSecretWasSet = YES;
1079 if ((currentHostName == NULL) || [currentHostName compare:hostNameString] != NSOrderedSame) {
1080 err = WriteHostname((CFDataRef)[self dataForDomain:hostNameString isEnabled:YES]);
1081 if (err != noErr) NSLog(@"WriteHostname returned %d\n", err);
1082 currentHostName = [hostNameString copy];
1083 } else if (hostSecretWasSet) {
1084 WriteHostname((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1085 usleep(200000); // Temporary hack
1086 if ([currentHostName length] > 0) WriteHostname((CFDataRef)[self dataForDomain:(NSString *)currentHostName isEnabled:YES]);
1089 // Save browse domain.
1090 if (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
1091 browseDomainData = [self dataForDomainArray:browseDomainsArray];
1092 err = WriteBrowseDomain((CFDataRef)browseDomainData);
1093 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", err);
1094 currentBrowseDomainsArray = [browseDomainsArray copy];
1097 // Save registration domain.
1098 if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) {
1100 err = WriteRegistrationDomain((CFDataRef)[self dataForDomain:regDomainString isEnabled:[wideAreaCheckBox state]]);
1101 if (err != noErr) NSLog(@"WriteRegistrationDomain returned %d\n", err);
1103 if (currentRegDomain) CFRelease(currentRegDomain);
1104 currentRegDomain = [regDomainString copy];
1106 if ([currentRegDomain length] > 0) {
1107 currentWideAreaState = [wideAreaCheckBox state];
1108 [registrationDataSource removeObject:regDomainString];
1109 [registrationDataSource addObject:currentRegDomain];
1110 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
1111 [regDomainsComboBox reloadData];
1113 currentWideAreaState = NO;
1114 [self toggleWideAreaBonjour:NO];
1115 if (defaultRegDomain != nil) [regDomainsComboBox setStringValue:defaultRegDomain];
1117 } else if (regSecretWasSet) {
1118 WriteRegistrationDomain((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1119 usleep(200000); // Temporary hack
1120 if ([currentRegDomain length] > 0) WriteRegistrationDomain((CFDataRef)[self dataForDomain:currentRegDomain isEnabled:currentWideAreaState]);
1125 - (NSPreferencePaneUnselectReply)shouldUnselect
1128 if (prefsNeedUpdating == YES) {
1130 [self disableControls];
1133 @"Apply Configuration Changes?",
1139 @selector( savePanelWillClose:returnCode:contextInfo: ),
1141 (void *) self, // sender,
1143 return NSUnselectLater;
1147 return NSUnselectNow;
1151 -(void)disableControls
1153 [hostName setEnabled:NO];
1154 [hostNameSharedSecretButton setEnabled:NO];
1155 [browseDomainsComboBox setEnabled:NO];
1156 [applyButton setEnabled:NO];
1157 [revertButton setEnabled:NO];
1158 [wideAreaCheckBox setEnabled:NO];
1159 [regDomainsComboBox setEnabled:NO];
1160 [registrationSharedSecretButton setEnabled:NO];
1161 [statusImageView setEnabled:NO];
1163 browseDomainListEnabled = NO;
1164 [browseDomainList deselectAll:self];
1165 [browseDomainList setEnabled:NO];
1167 [addBrowseDomainButton setEnabled:NO];
1168 [removeBrowseDomainButton setEnabled:NO];
1172 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
1174 return browseDomainListEnabled;
1178 -(void)enableControls
1180 [hostName setEnabled:YES];
1181 [hostNameSharedSecretButton setEnabled:YES];
1182 [browseDomainsComboBox setEnabled:YES];
1183 [wideAreaCheckBox setEnabled:YES];
1184 [registrationSharedSecretButton setEnabled:YES];
1185 [self toggleWideAreaBonjour:[wideAreaCheckBox state]];
1186 [statusImageView setEnabled:YES];
1187 [addBrowseDomainButton setEnabled:YES];
1189 [browseDomainList setEnabled:YES];
1190 [browseDomainList deselectAll:self];
1191 browseDomainListEnabled = YES;
1193 [removeBrowseDomainButton setEnabled:[browseDomainList numberOfSelectedRows]];
1194 [applyButton setEnabled:prefsNeedUpdating];
1195 [revertButton setEnabled:prefsNeedUpdating];
1199 - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view
1201 [self enableControls];
1205 - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view
1207 [self disableControls];