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):
44 $Log: DNSServiceDiscoveryPref.m,v $
45 Revision 1.6 2005/02/26 00:44:24 cheshire
46 Restore default reg domain if user deletes text and clicks "apply"
48 Revision 1.5 2005/02/25 02:29:28 cheshire
49 Show yellow dot for "update in progress"
51 Revision 1.4 2005/02/16 00:18:33 cheshire
54 Revision 1.3 2005/02/10 22:35:20 cheshire
55 <rdar://problem/3727944> Update name
57 Revision 1.2 2005/02/08 01:32:05 cheshire
58 Add trimCharactersFromDomain routine to strip leading and trailing
59 white space and punctuation from user-entered fields.
61 Revision 1.1 2005/02/05 01:59:19 cheshire
62 Add Preference Pane to facilitate testing of DDNS & wide-area features
66 #import "DNSServiceDiscoveryPref.h"
67 #import "ConfigurationAuthority.h"
68 #import "PrivilegedOperations.h"
71 @implementation DNSServiceDiscoveryPref
73 static CFComparisonResult
74 MyArrayCompareFunction(id val1, id val2, void *context)
76 return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive);
80 static CFComparisonResult
81 MyDomainArrayCompareFunction(id val1, id val2, void *context)
83 NSString *domain1 = [val1 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
84 NSString *domain2 = [val2 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
85 return CFStringCompare((CFStringRef)domain1, (CFStringRef)domain2, kCFCompareCaseInsensitive);
90 GetNextLabel(const char *cstr, char label[64])
93 while (*cstr && *cstr != '.') // While we have characters in the label...
99 if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1]))
101 int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal
102 int v1 = cstr[ 0] - '0';
103 int v2 = cstr[ 1] - '0';
104 int val = v0 * 100 + v1 * 10 + v2;
105 if (val <= 255) { c = (char)val; cstr += 2; } // If valid three-digit decimal value, use it
109 if (ptr >= label+64) return(NULL);
111 if (*cstr) cstr++; // Skip over the trailing dot (if present)
117 static void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
119 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
122 [me setupInitialValues];
126 static void ServiceDomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
127 DNSServiceErrorType errorCode, const char *replyDomain, void *context, DNSServiceFlags enumType)
129 if (strcmp(replyDomain, "local.") == 0) return; // local domain is not interesting
131 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
132 BOOL moreComing = (BOOL)(flags & kDNSServiceFlagsMoreComing);
133 NSMutableArray * domainArray;
134 NSMutableArray * defaultBrowseDomainsArray = nil;
135 NSComboBox * domainComboBox;
136 NSString * domainString;
137 NSString * currentDomain = nil;
138 char decodedDomainString[kDNSServiceMaxDomainName] = "\0";
139 char nextLabel[256] = "\0";
140 char * buffer = (char *)replyDomain;
143 buffer = (char *)GetNextLabel(buffer, nextLabel);
144 strcat(decodedDomainString, nextLabel);
145 strcat(decodedDomainString, ".");
148 // Remove trailing dot from domain name.
149 decodedDomainString[strlen(decodedDomainString)-1] = '\0';
151 domainString = [[[NSString alloc] initWithUTF8String:(const char *)decodedDomainString] autorelease];
153 if (enumType & kDNSServiceFlagsRegistrationDomains) {
154 domainArray = [me registrationDataSource];
155 domainComboBox = [me regDomainsComboBox];
156 currentDomain = [me currentRegDomain];
158 domainArray = [me browseDataSource];
159 domainComboBox = [me browseDomainsComboBox];
160 defaultBrowseDomainsArray = [me defaultBrowseDomainsArray];
163 if (flags & kDNSServiceFlagsAdd) {
164 [domainArray removeObject:domainString]; // How can I check if an object is in the array?
165 [domainArray addObject:domainString];
166 if ((flags & kDNSServiceFlagsDefault) && (enumType & kDNSServiceFlagsRegistrationDomains)) {
167 [me setDefaultRegDomain:domainString];
168 if ([[domainComboBox stringValue] length] == 0) [domainComboBox setStringValue:domainString];
169 } else if ((flags & kDNSServiceFlagsDefault) && !(enumType & kDNSServiceFlagsRegistrationDomains)) {
170 [defaultBrowseDomainsArray removeObject:domainString];
171 [defaultBrowseDomainsArray addObject:domainString];
175 if (moreComing == NO) {
176 [domainArray sortUsingFunction:MyArrayCompareFunction context:nil];
177 [domainComboBox reloadData];
183 browseDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
184 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
186 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsBrowseDomains);
191 registrationDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
192 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
194 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsRegistrationDomains);
200 MyDNSServiceCleanUp(MyDNSServiceState * query)
202 /* Remove the CFRunLoopSource from the current run loop. */
203 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
204 CFRelease(query->source);
206 /* Invalidate the CFSocket. */
207 CFSocketInvalidate(query->socket);
208 CFRelease(query->socket);
210 /* Workaround that gives time to CFSocket's select thread so it can remove the socket from its FD set
211 before we close the socket by calling DNSServiceRefDeallocate. <rdar://problem/3585273> */
214 /* Terminate the connection with the mDNSResponder daemon, which cancels the query. */
215 DNSServiceRefDeallocate(query->service);
221 MySocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void * data, void * info)
225 #pragma unused(address)
228 DNSServiceErrorType err;
230 MyDNSServiceState * query = (MyDNSServiceState *)info; // context passed in to CFSocketCreateWithNative().
231 assert(query != NULL);
233 /* Read a reply from the mDNSResponder. */
234 err= DNSServiceProcessResult(query->service);
235 if (err != kDNSServiceErr_NoError) {
236 fprintf(stderr, "DNSServiceProcessResult returned %d\n", err);
238 /* Terminate the query operation and release the CFRunLoopSource and CFSocket. */
239 MyDNSServiceCleanUp(query);
246 MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query)
248 CFSocketNativeHandle sock;
249 CFOptionFlags sockFlags;
250 CFSocketContext context = { 0, query, NULL, NULL, NULL }; // Use MyDNSServiceState as context data.
252 /* Access the underlying Unix domain socket to communicate with the mDNSResponder daemon. */
253 sock = DNSServiceRefSockFD(query->service);
256 /* Create a CFSocket using the Unix domain socket. */
257 query->socket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, MySocketReadCallback, &context);
258 assert(query->socket != NULL);
260 /* Prevent CFSocketInvalidate from closing DNSServiceRef's socket. */
261 sockFlags = CFSocketGetSocketFlags(query->socket);
262 CFSocketSetSocketFlags(query->socket, sockFlags & (~kCFSocketCloseOnInvalidate));
264 /* Create a CFRunLoopSource from the CFSocket. */
265 query->source = CFSocketCreateRunLoopSource(NULL, query->socket, 0);
266 assert(query->source != NULL);
268 /* Add the CFRunLoopSource to the current run loop. */
269 CFRunLoopAddSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
274 -(void)updateStatusImageView
276 int value = [self statusForHostName:currentHostName];
277 if (value == 0) [statusImageView setImage:successImage];
278 else if (value > 0) [statusImageView setImage:inprogressImage];
279 else [statusImageView setImage:failureImage];
283 - (void)watchForPreferenceChanges
285 SCDynamicStoreContext context = { 0, self, NULL, NULL, NULL };
286 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("watchForPreferenceChanges"), NetworkChanged, &context);
287 CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
288 CFRunLoopSourceRef rls;
290 assert(store != NULL);
291 assert(keys != NULL);
293 CFArrayAppendValue(keys, SC_DYNDNS_STATE_KEY);
294 CFArrayAppendValue(keys, SC_DYNDNS_SETUP_KEY);
296 (void)SCDynamicStoreSetNotificationKeys(store, keys, NULL);
298 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
301 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
308 -(int)statusForHostName:(NSString * )domain
310 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("statusForHostName"), NULL, NULL);
311 NSString *lowercaseDomain = [domain lowercaseString];
314 assert(store != NULL);
316 NSDictionary *dynamicDNS = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY);
318 NSDictionary *hostNames = [dynamicDNS objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
319 NSDictionary *infoDict = [hostNames objectForKey:lowercaseDomain];
320 if (infoDict) status = [[infoDict objectForKey:(NSString*)SC_DYNDNS_STATUS_KEY] intValue];
321 CFRelease(dynamicDNS);
329 - (void)startDomainBrowsing
331 DNSServiceFlags flags;
332 OSStatus err = noErr;
334 flags = kDNSServiceFlagsRegistrationDomains;
335 err = DNSServiceEnumerateDomains(®Query.service, flags, 0, registrationDomainReply, (void *)self);
336 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(®Query);
338 flags = kDNSServiceFlagsBrowseDomains;
339 err = DNSServiceEnumerateDomains(&browseQuery.service, flags, 0, browseDomainReply, (void *)self);
340 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(&browseQuery);
344 -(void)readPreferences
346 NSDictionary *origDict;
347 NSArray *regDomainArray;
350 if (currentRegDomain) [currentRegDomain release];
351 if (currentBrowseDomainsArray) [currentBrowseDomainsArray release];
352 if (currentHostName) [currentHostName release];
354 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL);
355 origDict = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY);
357 regDomainArray = [origDict objectForKey:(NSString *)SC_DYNDNS_REGDOMAINS_KEY];
358 if (regDomainArray && [regDomainArray count] > 0) {
359 currentRegDomain = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
360 currentWideAreaState = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
362 currentRegDomain = [[NSString alloc] initWithString:@""];
363 currentWideAreaState = NO;
366 currentBrowseDomainsArray = [[origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY] retain];
368 hostArray = [origDict objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
369 if (hostArray && [hostArray count] > 0) {
370 currentHostName = [[[hostArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
372 currentHostName = [[NSString alloc] initWithString:@""];
380 - (void)tableViewSelectionDidChange:(NSNotification *)notification;
382 [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]];
386 - (void)setBrowseDomainsComboBox;
388 NSString * domain = nil;
390 if ([defaultBrowseDomainsArray count] > 0) {
391 NSEnumerator * arrayEnumerator = [defaultBrowseDomainsArray objectEnumerator];
392 while (domain = [arrayEnumerator nextObject]) {
393 if ([self domainAlreadyInList:domain] == NO) break;
396 if (domain) [browseDomainsComboBox setStringValue:domain];
397 else [browseDomainsComboBox setStringValue:@""];
401 - (IBAction)addBrowseDomainClicked:(id)sender;
403 [self setBrowseDomainsComboBox];
405 [NSApp beginSheet:addBrowseDomainWindow modalForWindow:mainWindow modalDelegate:self
406 didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
408 [browseDomainList deselectAll:sender];
409 [self updateApplyButtonState];
413 - (IBAction)removeBrowseDomainClicked:(id)sender;
415 int selectedBrowseDomain = [browseDomainList selectedRow];
416 [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain];
417 [browseDomainList reloadData];
418 [self updateApplyButtonState];
422 - (IBAction)enableBrowseDomainClicked:(id)sender;
424 NSTableView *tableView = sender;
425 NSMutableDictionary *browseDomainDict;
428 browseDomainDict = [[browseDomainsArray objectAtIndex:[tableView clickedRow]] mutableCopy];
429 value = [[browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
430 [browseDomainDict setObject:[[[NSNumber alloc] initWithInt:(!value)] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
431 [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict];
432 [tableView reloadData];
433 [self updateApplyButtonState];
438 - (int)numberOfRowsInTableView:(NSTableView *)tableView;
440 int numberOfRows = 0;
442 if (browseDomainsArray) {
443 numberOfRows = [browseDomainsArray count];
449 - (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem;
451 [browseDomainList deselectAll:self];
452 [mainWindow makeFirstResponder:nil];
456 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row;
458 NSDictionary *browseDomainDict;
461 if (browseDomainsArray) {
462 browseDomainDict = [browseDomainsArray objectAtIndex:row];
463 if (browseDomainDict) {
464 if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_ENABLED_KEY]) {
465 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
466 } else if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_DOMAIN_KEY]) {
467 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
475 - (void)setupInitialValues
477 [self readPreferences];
479 if (currentHostName) {
480 [hostName setStringValue:currentHostName];
481 [self updateStatusImageView];
484 if (browseDomainsArray) {
485 [browseDomainsArray release];
486 browseDomainsArray = nil;
489 if (currentBrowseDomainsArray) {
490 browseDomainsArray = [currentBrowseDomainsArray mutableCopy];
491 if (browseDomainsArray) {
492 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
493 if ([browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
494 OSStatus err = WriteBrowseDomain((CFDataRef)[self dataForDomainArray:browseDomainsArray]);
495 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", err);
496 [currentBrowseDomainsArray release];
497 currentBrowseDomainsArray = [browseDomainsArray copy];
501 browseDomainsArray = nil;
503 [browseDomainList reloadData];
505 if (currentRegDomain && ([currentRegDomain length] > 0)) {
506 [regDomainsComboBox setStringValue:currentRegDomain];
507 [registrationDataSource removeObject:currentRegDomain];
508 [registrationDataSource addObject:currentRegDomain];
509 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
510 [regDomainsComboBox reloadData];
513 if (currentWideAreaState) {
514 [self toggleWideAreaBonjour:YES];
516 [self toggleWideAreaBonjour:NO];
519 if (hostNameSharedSecretValue) {
520 [hostNameSharedSecretValue release];
521 hostNameSharedSecretValue = nil;
524 if (regSharedSecretValue) {
525 [regSharedSecretValue release];
526 regSharedSecretValue = nil;
529 [self updateApplyButtonState];
530 [mainWindow makeFirstResponder:nil];
531 [browseDomainList deselectAll:self];
532 [removeBrowseDomainButton setEnabled:NO];
541 prefsNeedUpdating = NO;
543 browseDomainListEnabled = NO;
544 defaultRegDomain = nil;
545 currentRegDomain = nil;
546 currentBrowseDomainsArray = nil;
547 currentHostName = nil;
548 hostNameSharedSecretValue = nil;
549 regSharedSecretValue = nil;
550 browseDomainsArray = nil;
551 justStartedEditing = YES;
552 currentWideAreaState = NO;
553 NSString *successPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"success" ofType:@"tiff"];
554 NSString *inprogressPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"inprogress" ofType:@"tiff"];
555 NSString *failurePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"failure" ofType:@"tiff"];
557 registrationDataSource = [[NSMutableArray alloc] init];
558 browseDataSource = [[NSMutableArray alloc] init];
559 defaultBrowseDomainsArray = [[NSMutableArray alloc] init];
560 successImage = [[NSImage alloc] initWithContentsOfFile:successPath];
561 inprogressImage = [[NSImage alloc] initWithContentsOfFile:inprogressPath];
562 failureImage = [[NSImage alloc] initWithContentsOfFile:failurePath];
564 [tabView selectFirstTabViewItem:self];
565 [self setupInitialValues];
566 [self startDomainBrowsing];
567 [self watchForPreferenceChanges];
569 [tabView setDelegate:self];
571 InitConfigAuthority();
572 err = EnsureToolInstalled();
573 if (err == noErr) toolInstalled = YES;
574 else fprintf(stderr, "EnsureToolInstalled returned %ld\n", err);
579 - (IBAction)closeMyCustomSheet:(id)sender
581 BOOL result = [sender isEqualTo:browseOKButton] || [sender isEqualTo:secretOKButton];
583 if (result) [NSApp endSheet:[sender window] returnCode:NSOKButton];
584 else [NSApp endSheet:[sender window] returnCode:NSCancelButton];
588 - (void)sharedSecretSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
590 NSButton * button = (NSButton *)contextInfo;
591 [sheet orderOut:self];
592 [self enableControls];
594 if (returnCode == NSOKButton) {
595 if ([button isEqualTo:hostNameSharedSecretButton]) {
596 hostNameSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
597 hostNameSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
599 regSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
600 regSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
602 [self updateApplyButtonState];
604 [sharedSecretValue setStringValue:@""];
608 - (BOOL)domainAlreadyInList:(NSString *)domainString
610 if (browseDomainsArray) {
611 NSDictionary *domainDict;
612 NSString *domainName;
613 NSEnumerator *arrayEnumerator = [browseDomainsArray objectEnumerator];
614 while (domainDict = [arrayEnumerator nextObject]) {
615 domainName = [domainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
616 if ([domainString caseInsensitiveCompare:domainName] == NSOrderedSame) return YES;
623 - (NSString *)trimCharactersFromDomain:(NSString *)domain
625 NSMutableCharacterSet * trimSet = [[[NSCharacterSet whitespaceCharacterSet] mutableCopy] autorelease];
626 [trimSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
627 return [domain stringByTrimmingCharactersInSet:trimSet];
631 - (void)addBrowseDomainSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
633 [sheet orderOut:self];
634 [self enableControls];
636 if (returnCode == NSOKButton) {
637 NSString * newBrowseDomainString = [self trimCharactersFromDomain:[browseDomainsComboBox stringValue]];
638 NSMutableDictionary *newBrowseDomainDict;
640 if (browseDomainsArray == nil) browseDomainsArray = [[NSMutableArray alloc] initWithCapacity:0];
641 if ([self domainAlreadyInList:newBrowseDomainString] == NO) {
642 newBrowseDomainDict = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
644 [newBrowseDomainDict setObject:newBrowseDomainString forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
645 [newBrowseDomainDict setObject:[[[NSNumber alloc] initWithBool:YES] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
647 [browseDomainsArray addObject:newBrowseDomainDict];
648 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
649 [browseDomainList reloadData];
650 [self updateApplyButtonState];
656 -(void)validateTextFields
658 [hostName validateEditing];
659 [browseDomainsComboBox validateEditing];
660 [regDomainsComboBox validateEditing];
664 - (IBAction)changeButtonPressed:(id)sender
668 [self disableControls];
669 [self validateTextFields];
670 [mainWindow makeFirstResponder:nil];
671 [browseDomainList deselectAll:sender];
673 if ([sender isEqualTo:hostNameSharedSecretButton]) {
674 if (hostNameSharedSecretValue) {
675 [sharedSecretValue setStringValue:hostNameSharedSecretValue];
676 } else if (keyName = [self sharedSecretKeyName:[hostName stringValue]]) {
677 [sharedSecretName setStringValue:keyName];
678 [sharedSecretValue setStringValue:@"****************"];
680 [sharedSecretName setStringValue:[hostName stringValue]];
681 [sharedSecretValue setStringValue:@""];
685 if (regSharedSecretValue) {
686 [sharedSecretValue setStringValue:regSharedSecretValue];
687 } else if (keyName = [self sharedSecretKeyName:[regDomainsComboBox stringValue]]) {
688 [sharedSecretName setStringValue:keyName];
689 [sharedSecretValue setStringValue:@"****************"];
691 [sharedSecretName setStringValue:[regDomainsComboBox stringValue]];
692 [sharedSecretValue setStringValue:@""];
696 [sharedSecretWindow resignFirstResponder];
698 if ([[sharedSecretName stringValue] length] > 0) [sharedSecretWindow makeFirstResponder:sharedSecretValue];
699 else [sharedSecretWindow makeFirstResponder:sharedSecretName];
701 [NSApp beginSheet:sharedSecretWindow modalForWindow:mainWindow modalDelegate:self
702 didEndSelector:@selector(sharedSecretSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
706 - (IBAction)wideAreaCheckBoxChanged:(id)sender
708 [self toggleWideAreaBonjour:[sender state]];
709 [self updateApplyButtonState];
710 [mainWindow makeFirstResponder:nil];
714 - (void)updateApplyButtonState
716 NSString *hostNameString = [hostName stringValue];
717 NSString *regDomainString = [regDomainsComboBox stringValue];
719 NSComparisonResult hostNameResult = [hostNameString compare:currentHostName];
720 NSComparisonResult regDomainResult = [regDomainString compare:currentRegDomain];
722 if ((currentHostName && (hostNameResult != NSOrderedSame)) ||
723 (currentRegDomain && (regDomainResult != NSOrderedSame) && ([wideAreaCheckBox state])) ||
724 (currentHostName == nil && ([hostNameString length]) > 0) ||
725 (currentRegDomain == nil && ([regDomainString length]) > 0) ||
726 (currentWideAreaState != [wideAreaCheckBox state]) ||
727 (hostNameSharedSecretValue != nil) ||
728 (regSharedSecretValue != nil) ||
729 (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO))
731 [self enableApplyButton];
733 [self disableApplyButton];
739 - (void)controlTextDidChange:(NSNotification *)notification;
741 [self updateApplyButtonState];
746 - (IBAction)comboAction:(id)sender;
748 [self updateApplyButtonState];
752 - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)index
754 NSString *domain = nil;
755 if ([aComboBox isEqualTo:browseDomainsComboBox]) domain = [browseDataSource objectAtIndex:index];
756 else if ([aComboBox isEqualTo:regDomainsComboBox]) domain = [registrationDataSource objectAtIndex:index];
762 - (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox
765 if ([aComboBox isEqualTo:browseDomainsComboBox]) count = [browseDataSource count];
766 else if ([aComboBox isEqualTo:regDomainsComboBox]) count = [registrationDataSource count];
771 - (NSMutableArray *)browseDataSource
773 return browseDataSource;
777 - (NSMutableArray *)registrationDataSource
779 return registrationDataSource;
783 - (NSComboBox *)browseDomainsComboBox
785 return browseDomainsComboBox;
789 - (NSComboBox *)regDomainsComboBox
791 return regDomainsComboBox;
795 - (NSString *)currentRegDomain
797 return currentRegDomain;
801 - (NSMutableArray *)defaultBrowseDomainsArray
803 return defaultBrowseDomainsArray;
807 - (NSArray *)currentBrowseDomainsArray
809 return currentBrowseDomainsArray;
813 - (NSString *)currentHostName
815 return currentHostName;
819 - (NSString *)defaultRegDomain
821 return defaultRegDomain;
825 - (void)setDefaultRegDomain:(NSString *)domain
827 [defaultRegDomain release];
828 defaultRegDomain = domain;
829 [defaultRegDomain retain];
836 mainWindow = [[self mainView] window];
840 - (void)mainViewDidLoad
842 [comboAuthButton setString:"system.preferences"];
843 [comboAuthButton setDelegate:self];
844 [comboAuthButton updateStatus:nil];
845 [comboAuthButton setAutoupdate:YES];
850 - (IBAction)applyClicked:(id)sender
852 [self applyCurrentState];
856 - (void)applyCurrentState
858 [self validateTextFields];
860 if (toolInstalled == YES) {
861 [self savePreferences];
862 [self disableApplyButton];
863 [mainWindow makeFirstResponder:nil];
868 - (void)enableApplyButton
870 [applyButton setEnabled:YES];
871 [revertButton setEnabled:YES];
872 prefsNeedUpdating = YES;
876 - (void)disableApplyButton
878 [applyButton setEnabled:NO];
879 [revertButton setEnabled:NO];
880 prefsNeedUpdating = NO;
884 - (void)toggleWideAreaBonjour:(BOOL)state
886 [wideAreaCheckBox setState:state];
887 [regDomainsComboBox setEnabled:state];
888 [registrationSharedSecretButton setEnabled:state];
892 - (IBAction)revertClicked:(id)sender;
894 [self restorePreferences];
895 [browseDomainList deselectAll:sender];
896 [mainWindow makeFirstResponder:nil];
900 - (void)restorePreferences
902 [self setupInitialValues];
906 - (void)savePanelWillClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
908 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)contextInfo;
910 if (returnCode == NSAlertDefaultReturn) {
911 [me applyCurrentState];
912 } else if (returnCode == NSAlertAlternateReturn ) {
913 [me restorePreferences];
917 [me replyToShouldUnselect:(returnCode != NSAlertOtherReturn)];
921 -(SecKeychainItemRef)copyKeychainItemforDomain:(NSString *)domain
923 const char * serviceName = [domain UTF8String];
924 UInt32 type = 'ddns';
925 UInt32 typeLength = sizeof(type);
927 SecKeychainAttribute attrs[] = { { kSecServiceItemAttr, strlen(serviceName), (char *)serviceName },
928 { kSecTypeItemAttr, typeLength, (UInt32 *)&type } };
930 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
931 SecKeychainSearchRef searchRef;
932 SecKeychainItemRef itemRef = NULL;
935 err = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef);
937 err = SecKeychainSearchCopyNext(searchRef, &itemRef);
938 if (err != noErr) itemRef = NULL;
944 -(NSString *)sharedSecretKeyName:(NSString * )domain
946 SecKeychainItemRef itemRef = NULL;
947 NSString *keyName = nil;
950 err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
951 assert(err == noErr);
953 itemRef = [self copyKeychainItemforDomain:[domain lowercaseString]];
956 SecKeychainAttributeInfo attrInfo;
957 SecKeychainAttributeList *attrList = NULL;
958 SecKeychainAttribute attribute;
961 tags[0] = kSecAccountItemAttr;
964 attrInfo.format = NULL;
966 err = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, NULL, NULL);
968 for (i = 0; i < attrList->count; i++) {
969 attribute = attrList->attr[i];
970 if (attribute.tag == kSecAccountItemAttr) {
971 keyName = [[NSString alloc] initWithBytes:attribute.data length:attribute.length encoding:NSUTF8StringEncoding];
975 if (attrList) (void)SecKeychainItemFreeAttributesAndData(attrList, NULL);
983 -(NSString *)domainForHostName:(NSString *)hostNameString
985 NSString * domainName = nil;
989 ptr = (char *)[hostNameString UTF8String];
991 ptr = (char *)GetNextLabel(ptr, text);
992 domainName = [[NSString alloc] initWithUTF8String:(const char *)ptr];
994 return ([domainName autorelease]);
998 - (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled
1000 NSMutableArray *domainsArray;
1001 NSMutableDictionary *domainDict = nil;
1003 if (domainName && [domainName length] > 0) {
1004 domainDict= [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
1005 [domainDict setObject:domainName forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
1006 [domainDict setObject:[[[NSNumber alloc] initWithBool:enabled] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
1008 domainsArray = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
1009 if (domainDict) [domainsArray addObject:domainDict];
1010 return [NSArchiver archivedDataWithRootObject:domainsArray];
1014 - (NSData *)dataForDomainArray:(NSArray *)domainArray
1016 return [NSArchiver archivedDataWithRootObject:domainArray];
1020 - (NSData *)dataForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName
1022 NSMutableDictionary *sharedSecretDict = [[[NSMutableDictionary alloc] initWithCapacity:3] autorelease];
1023 [sharedSecretDict setObject:secret forKey:(NSString *)SC_DYNDNS_SECRET_KEY];
1024 [sharedSecretDict setObject:[domainName lowercaseString] forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
1025 [sharedSecretDict setObject:keyName forKey:(NSString *)SC_DYNDNS_KEYNAME_KEY];
1026 return [NSArchiver archivedDataWithRootObject:sharedSecretDict];
1030 -(void)savePreferences
1032 NSString *hostNameString = [hostName stringValue];
1033 NSString *browseDomainString = [browseDomainsComboBox stringValue];
1034 NSString *regDomainString = [regDomainsComboBox stringValue];
1035 NSString *tempHostNameSharedSecretName = hostNameSharedSecretName;
1036 NSString *tempRegSharedSecretName = regSharedSecretName;
1037 NSData *browseDomainData = nil;
1038 BOOL regSecretWasSet = NO;
1039 BOOL hostSecretWasSet = NO;
1040 OSStatus err = noErr;
1042 hostNameString = [self trimCharactersFromDomain:hostNameString];
1043 browseDomainString = [self trimCharactersFromDomain:browseDomainString];
1044 regDomainString = [self trimCharactersFromDomain:regDomainString];
1045 tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName];
1046 tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName];
1048 [hostName setStringValue:hostNameString];
1049 [regDomainsComboBox setStringValue:regDomainString];
1051 // Convert Shared Secret account names to lowercase.
1052 tempHostNameSharedSecretName = [tempHostNameSharedSecretName lowercaseString];
1053 tempRegSharedSecretName = [tempRegSharedSecretName lowercaseString];
1055 // Save hostname shared secret.
1056 if ([hostNameSharedSecretName length] > 0 && ([hostNameSharedSecretValue length] > 0)) {
1057 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:hostNameSharedSecretValue domain:hostNameString key:tempHostNameSharedSecretName]);
1058 [hostNameSharedSecretValue release];
1059 hostNameSharedSecretValue = nil;
1060 hostSecretWasSet = YES;
1063 // Save registration domain shared secret.
1064 if (([regSharedSecretName length] > 0) && ([regSharedSecretValue length] > 0)) {
1065 SetKeyForDomain((CFDataRef)[self dataForSharedSecret:regSharedSecretValue domain:regDomainString key:tempRegSharedSecretName]);
1066 [regSharedSecretValue release];
1067 regSharedSecretValue = nil;
1068 regSecretWasSet = YES;
1072 if ((currentHostName == NULL) || [currentHostName compare:hostNameString] != NSOrderedSame) {
1073 err = WriteHostname((CFDataRef)[self dataForDomain:hostNameString isEnabled:YES]);
1074 if (err != noErr) NSLog(@"WriteHostname returned %d\n", err);
1075 currentHostName = [hostNameString copy];
1076 } else if (hostSecretWasSet) {
1077 WriteHostname((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1078 usleep(200000); // Temporary hack
1079 if ([currentHostName length] > 0) WriteHostname((CFDataRef)[self dataForDomain:(NSString *)currentHostName isEnabled:YES]);
1082 // Save browse domain.
1083 if (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
1084 browseDomainData = [self dataForDomainArray:browseDomainsArray];
1085 err = WriteBrowseDomain((CFDataRef)browseDomainData);
1086 if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", err);
1087 currentBrowseDomainsArray = [browseDomainsArray copy];
1090 // Save registration domain.
1091 if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) {
1093 err = WriteRegistrationDomain((CFDataRef)[self dataForDomain:regDomainString isEnabled:[wideAreaCheckBox state]]);
1094 if (err != noErr) NSLog(@"WriteRegistrationDomain returned %d\n", err);
1096 if (currentRegDomain) CFRelease(currentRegDomain);
1097 currentRegDomain = [regDomainString copy];
1099 if ([currentRegDomain length] > 0) {
1100 currentWideAreaState = [wideAreaCheckBox state];
1101 [registrationDataSource removeObject:regDomainString];
1102 [registrationDataSource addObject:currentRegDomain];
1103 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
1104 [regDomainsComboBox reloadData];
1106 currentWideAreaState = NO;
1107 [self toggleWideAreaBonjour:NO];
1108 if (defaultRegDomain != nil) [regDomainsComboBox setStringValue:defaultRegDomain];
1110 } else if (regSecretWasSet) {
1111 WriteRegistrationDomain((CFDataRef)[self dataForDomain:@"" isEnabled:NO]);
1112 usleep(200000); // Temporary hack
1113 if ([currentRegDomain length] > 0) WriteRegistrationDomain((CFDataRef)[self dataForDomain:currentRegDomain isEnabled:currentWideAreaState]);
1118 - (NSPreferencePaneUnselectReply)shouldUnselect
1121 if (prefsNeedUpdating == YES) {
1123 [self disableControls];
1126 @"Apply Configuration Changes?",
1132 @selector( savePanelWillClose:returnCode:contextInfo: ),
1134 (void *) self, // sender,
1136 return NSUnselectLater;
1140 return NSUnselectNow;
1144 -(void)disableControls
1146 [hostName setEnabled:NO];
1147 [hostNameSharedSecretButton setEnabled:NO];
1148 [browseDomainsComboBox setEnabled:NO];
1149 [applyButton setEnabled:NO];
1150 [revertButton setEnabled:NO];
1151 [wideAreaCheckBox setEnabled:NO];
1152 [regDomainsComboBox setEnabled:NO];
1153 [registrationSharedSecretButton setEnabled:NO];
1154 [statusImageView setEnabled:NO];
1156 browseDomainListEnabled = NO;
1157 [browseDomainList deselectAll:self];
1158 [browseDomainList setEnabled:NO];
1160 [addBrowseDomainButton setEnabled:NO];
1161 [removeBrowseDomainButton setEnabled:NO];
1165 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
1167 return browseDomainListEnabled;
1171 -(void)enableControls
1173 [hostName setEnabled:YES];
1174 [hostNameSharedSecretButton setEnabled:YES];
1175 [browseDomainsComboBox setEnabled:YES];
1176 [wideAreaCheckBox setEnabled:YES];
1177 [registrationSharedSecretButton setEnabled:YES];
1178 [self toggleWideAreaBonjour:[wideAreaCheckBox state]];
1179 [statusImageView setEnabled:YES];
1180 [addBrowseDomainButton setEnabled:YES];
1182 [browseDomainList setEnabled:YES];
1183 [browseDomainList deselectAll:self];
1184 browseDomainListEnabled = YES;
1186 [removeBrowseDomainButton setEnabled:[browseDomainList numberOfSelectedRows]];
1187 [applyButton setEnabled:prefsNeedUpdating];
1188 [revertButton setEnabled:prefsNeedUpdating];
1192 - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view
1194 [self enableControls];
1198 - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view
1200 [self disableControls];