]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m
mDNSResponder-1096.0.2.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / PreferencePane / DNSServiceDiscoveryPref.m
1 /*
2 File: DNSServiceDiscoveryPref.m
3
4 Abstract: System Preference Pane for Dynamic DNS and Wide-Area DNS Service Discovery
5
6 Copyright: (c) Copyright 2005-2011 Apple Inc. All rights reserved.
7
8 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 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.
13
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 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.
28
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.
34
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.
42 */
43
44 #import "DNSServiceDiscoveryPref.h"
45 #import "CNDomainBrowserView.h"
46 #import "BonjourSCStore.h"
47 #import "BonjourPrefTool.h"
48 #import <Foundation/NSXPCConnection_Private.h>
49
50 #include "../../Clients/ClientCommon.h"
51
52 #pragma mark - BonjourPrefTool
53
54 static OSStatus
55 DNSPrefTool_SetKeychainEntry(NSDictionary * secretDictionary)
56 {
57 __block OSStatus result;
58 BonjourPrefTool * prefTool;
59
60 NSXPCConnection * _connectionToTool = [[NSXPCConnection alloc] initWithServiceName:@"com.apple.preference.bonjour.tool"];
61 _connectionToTool.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(BonjourPrefToolProtocol)];
62 [_connectionToTool resume];
63
64 #if 0
65 prefTool = [_connectionToTool remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
66 NSLog( @"Cannot connect to BonjourPrefTool: %@.", error);
67 result = error.code;
68 }];
69 #else
70 prefTool = [_connectionToTool synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
71 NSLog( @"Cannot connect to BonjourPrefTool: %@.", error);
72 result = error.code;
73 }];
74 #endif
75 [prefTool setKeychainEntry: secretDictionary withStatus: ^(OSStatus status){
76 result = status;
77 }];
78
79 [_connectionToTool invalidate];
80
81 return (result);
82 }
83
84 #pragma mark -
85
86 @implementation DNSServiceDiscoveryPref
87
88 static NSInteger
89 MyArrayCompareFunction(id val1, id val2, void *context)
90 {
91 (void)context; // Unused
92 return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive);
93 }
94
95 static NSInteger
96 MyDomainArrayCompareFunction(id val1, id val2, void *context)
97 {
98 (void)context; // Unused
99 NSString *domain1 = [val1 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
100 NSString *domain2 = [val2 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
101 return CFStringCompare((CFStringRef)domain1, (CFStringRef)domain2, kCFCompareCaseInsensitive);
102 }
103
104
105 static void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
106 {
107 (void)store; // Unused
108 (void)changedKeys; // Unused
109 DNSServiceDiscoveryPref * me = (__bridge DNSServiceDiscoveryPref *)context;
110 assert(me != NULL);
111
112 [me setupInitialValues];
113 }
114
115
116 -(void)updateStatusImageView
117 {
118 int value = [self statusForHostName:currentHostName];
119 if (value == 0) [statusImageView setImage:successImage];
120 else if (value > 0) [statusImageView setImage:inprogressImage];
121 else [statusImageView setImage:failureImage];
122 }
123
124
125 - (void)watchForPreferenceChanges
126 {
127 SCDynamicStoreContext context = { 0, (__bridge void * _Nullable)(self), NULL, NULL, NULL };
128 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("watchForPreferenceChanges"), NetworkChanged, &context);
129 CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
130 CFRunLoopSourceRef rls;
131
132 assert(store != NULL);
133 assert(keys != NULL);
134
135 CFArrayAppendValue(keys, SC_DYNDNS_STATE_KEY);
136 CFArrayAppendValue(keys, SC_DYNDNS_SETUP_KEY);
137
138 (void)SCDynamicStoreSetNotificationKeys(store, keys, NULL);
139
140 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
141 assert(rls != NULL);
142
143 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
144 CFRelease(rls);
145
146 CFRelease(keys);
147 CFRelease(store);
148 }
149
150
151 -(int)statusForHostName:(NSString * )domain
152 {
153 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("statusForHostName"), NULL, NULL);
154 NSString *lowercaseDomain = [domain lowercaseString];
155 int status = 1;
156
157 assert(store != NULL);
158
159 NSDictionary *dynamicDNS = (NSDictionary *)CFBridgingRelease(SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY));
160 if (dynamicDNS) {
161 NSDictionary *hostNames = [dynamicDNS objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
162 NSDictionary *infoDict = [hostNames objectForKey:lowercaseDomain];
163 if (infoDict) status = [[infoDict objectForKey:(NSString*)SC_DYNDNS_STATUS_KEY] intValue];
164 }
165 CFRelease(store);
166
167 return status;
168 }
169
170
171 -(void)readPreferences
172 {
173 NSDictionary *origDict;
174 NSArray *regDomainArray;
175 NSArray *hostArray;
176
177 if (currentRegDomain) currentRegDomain = nil;
178 if (currentBrowseDomainsArray) currentBrowseDomainsArray = nil;
179 if (currentHostName) currentHostName = nil;
180
181 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL);
182 origDict = (NSDictionary *)CFBridgingRelease(SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY));
183
184 regDomainArray = [origDict objectForKey:(NSString *)SC_DYNDNS_REGDOMAINS_KEY];
185 if (regDomainArray && [regDomainArray count] > 0) {
186 currentRegDomain = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
187 currentWideAreaState = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
188 } else {
189 currentRegDomain = @"";
190 currentWideAreaState = NO;
191 }
192
193 currentBrowseDomainsArray = [origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY];
194
195 hostArray = [origDict objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY];
196 if (hostArray && [hostArray count] > 0) {
197 currentHostName = [[[hostArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy];
198 } else {
199 currentHostName = @"";
200 }
201
202 if (store) CFRelease(store);
203 }
204
205
206 - (void)tableViewSelectionDidChange:(NSNotification *)notification
207 {
208 [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]];
209 }
210
211
212 - (IBAction)addBrowseDomainClicked:(id)sender
213 {
214 NSWindow * window = (([NSEvent modifierFlags] & NSAlternateKeyMask) == NSAlternateKeyMask) ? addBrowseDomainManualWindow : addBrowseDomainWindow;
215 [browseDomainTextField setStringValue: [NSString string]];
216
217 [self disableControls];
218 [NSApp beginSheet:window modalForWindow:mainWindow modalDelegate:self
219 didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:(__bridge void * _Null_unspecified)(sender)];
220
221 [browseDomainList deselectAll:sender];
222 [self updateApplyButtonState];
223 }
224
225
226 - (IBAction)removeBrowseDomainClicked:(id)sender
227 {
228 (void)sender; // Unused
229 int selectedBrowseDomain = [browseDomainList selectedRow];
230 [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain];
231 [browseDomainList reloadData];
232 [self updateApplyButtonState];
233 }
234
235
236 - (IBAction)enableBrowseDomainClicked:(id)sender
237 {
238 NSTableView *tableView = sender;
239 NSMutableDictionary *browseDomainDict;
240 NSInteger value;
241
242 browseDomainDict = [[browseDomainsArray objectAtIndex:[tableView clickedRow]] mutableCopy];
243 value = [[browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
244 [browseDomainDict setObject:[NSNumber numberWithInt:(!value)] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
245 [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict];
246 [tableView reloadData];
247 [self updateApplyButtonState];
248 }
249
250
251
252 - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
253 {
254 (void)tableView; // Unused
255 int numberOfRows = 0;
256
257 if (browseDomainsArray) {
258 numberOfRows = [browseDomainsArray count];
259 }
260 return numberOfRows;
261 }
262
263
264 - (void)tabView:(NSTabView *)xtabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
265 {
266 (void)xtabView; // Unused
267 (void)tabViewItem; // Unused
268 [browseDomainList deselectAll:self];
269 [mainWindow makeFirstResponder:nil];
270 }
271
272
273 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
274 {
275 (void)tableView; // Unused
276 NSDictionary *browseDomainDict;
277 id value = nil;
278
279 if (browseDomainsArray) {
280 browseDomainDict = [browseDomainsArray objectAtIndex:row];
281 if (browseDomainDict) {
282 if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_ENABLED_KEY]) {
283 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
284 } else if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_DOMAIN_KEY]) {
285 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
286 }
287 }
288 }
289 return value;
290 }
291
292
293 - (void)setupInitialValues
294 {
295 [self readPreferences];
296
297 if (currentHostName) {
298 [hostName setStringValue:currentHostName];
299 [self updateStatusImageView];
300 }
301
302 if (browseDomainsArray) {
303 browseDomainsArray = nil;
304 }
305
306 if (currentBrowseDomainsArray) {
307 browseDomainsArray = [currentBrowseDomainsArray mutableCopy];
308 if (browseDomainsArray) {
309 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
310 if ([browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
311 [BonjourSCStore setObject: browseDomainsArray forKey: (NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY];
312 currentBrowseDomainsArray = [browseDomainsArray copy];
313 }
314 }
315 } else {
316 browseDomainsArray = nil;
317 }
318 [browseDomainList reloadData];
319
320 if (currentRegDomain && ([currentRegDomain length] > 0)) {
321 regDomainView.domain = currentRegDomain;
322 }
323
324 if (currentWideAreaState) {
325 [self toggleWideAreaBonjour:YES];
326 } else {
327 [self toggleWideAreaBonjour:NO];
328 }
329
330 if (hostNameSharedSecretValue) {
331 hostNameSharedSecretValue = nil;
332 }
333
334 if (regSharedSecretValue) {
335 regSharedSecretValue = nil;
336 }
337
338 [self updateApplyButtonState];
339 [mainWindow makeFirstResponder:nil];
340 [browseDomainList deselectAll:self];
341 [removeBrowseDomainButton setEnabled:NO];
342 }
343
344
345
346 - (void)awakeFromNib
347 {
348 prefsNeedUpdating = NO;
349 browseDomainListEnabled = NO;
350 defaultRegDomain = nil;
351 currentRegDomain = nil;
352 currentBrowseDomainsArray = nil;
353 currentHostName = nil;
354 hostNameSharedSecretValue = nil;
355 regSharedSecretValue = nil;
356 browseDomainsArray = nil;
357 currentWideAreaState = NO;
358 NSString *successPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"success" ofType:@"tiff"];
359 NSString *inprogressPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"inprogress" ofType:@"tiff"];
360 NSString *failurePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"failure" ofType:@"tiff"];
361
362 registrationDataSource = [[NSMutableArray alloc] init];
363 successImage = [[NSImage alloc] initWithContentsOfFile:successPath];
364 inprogressImage = [[NSImage alloc] initWithContentsOfFile:inprogressPath];
365 failureImage = [[NSImage alloc] initWithContentsOfFile:failurePath];
366
367 [tabView selectFirstTabViewItem:self];
368 [self setupInitialValues];
369 [self watchForPreferenceChanges];
370
371 }
372
373 - (void)willSelect
374 {
375 [super willSelect];
376 [bonjourBrowserView startBrowse];
377 [registrationBrowserView startBrowse];
378
379 }
380
381 - (void)willUnselect
382 {
383 [super willUnselect];
384 [bonjourBrowserView stopBrowse];
385 [registrationBrowserView stopBrowse];
386 }
387
388 - (IBAction)closeMyCustomSheet:(id)sender
389 {
390 BOOL result = [sender tag];
391
392 if (result) [NSApp endSheet:[sender window] returnCode:NSOKButton];
393 else [NSApp endSheet:[sender window] returnCode:NSCancelButton];
394 }
395
396
397 - (void)sharedSecretSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
398 {
399 NSButton * button = (__bridge NSButton *)contextInfo;
400 [sheet orderOut:self];
401 [self enableControls];
402
403 if (returnCode == NSOKButton) {
404 if ([button isEqualTo:hostNameSharedSecretButton]) {
405 hostNameSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
406 hostNameSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
407 } else {
408 regSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
409 regSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
410 }
411 [self updateApplyButtonState];
412 }
413 [sharedSecretValue setStringValue:@""];
414 }
415
416
417 - (BOOL)domainAlreadyInList:(NSString *)domainString
418 {
419 if (browseDomainsArray) {
420 NSDictionary *domainDict;
421 NSString *domainName;
422 NSEnumerator *arrayEnumerator = [browseDomainsArray objectEnumerator];
423 while ((domainDict = [arrayEnumerator nextObject]) != NULL) {
424 domainName = [domainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
425 if ([domainString caseInsensitiveCompare:domainName] == NSOrderedSame) return YES;
426 }
427 }
428 return NO;
429 }
430
431
432 - (NSString *)trimCharactersFromDomain:(NSString *)domain
433 {
434 NSMutableCharacterSet * trimSet = [NSMutableCharacterSet whitespaceCharacterSet];
435 [trimSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
436 return [domain stringByTrimmingCharactersInSet:trimSet];
437 }
438
439
440 - (void)addBrowseDomainSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
441 {
442 (void)contextInfo; // Unused
443 [sheet orderOut:self];
444 [self enableControls];
445
446 if (returnCode == NSOKButton) {
447 NSString * newBrowseDomainString;
448 if(sheet == addBrowseDomainManualWindow) newBrowseDomainString = [self trimCharactersFromDomain:[browseDomainTextField stringValue]];
449 else newBrowseDomainString = [self trimCharactersFromDomain:bonjourBrowserView.selectedDNSDomain];
450 NSMutableDictionary *newBrowseDomainDict;
451 if (browseDomainsArray == nil) browseDomainsArray = [[NSMutableArray alloc] initWithCapacity:0];
452 if ([self domainAlreadyInList:newBrowseDomainString] == NO) {
453 newBrowseDomainDict = [[NSMutableDictionary alloc] initWithCapacity:2];
454
455 [newBrowseDomainDict setObject:newBrowseDomainString forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
456 [newBrowseDomainDict setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
457
458 [browseDomainsArray addObject:newBrowseDomainDict];
459 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
460 [browseDomainList reloadData];
461 [self updateApplyButtonState];
462 }
463 }
464 }
465
466 -(void)validateTextFields
467 {
468 [hostName validateEditing];
469 [browseDomainTextField validateEditing];
470 }
471
472
473 - (IBAction)changeButtonPressed:(id)sender
474 {
475 NSString * keyName;
476
477 [self disableControls];
478 [self validateTextFields];
479 [mainWindow makeFirstResponder:nil];
480 [browseDomainList deselectAll:sender];
481
482 if ([sender isEqualTo:hostNameSharedSecretButton]) {
483 if (hostNameSharedSecretValue) {
484 [sharedSecretValue setStringValue:hostNameSharedSecretValue];
485 } else if ((keyName = [self sharedSecretKeyName:[hostName stringValue]]) != NULL) {
486 [sharedSecretName setStringValue:keyName];
487 [sharedSecretValue setStringValue:@"****************"];
488 } else {
489 [sharedSecretName setStringValue:[hostName stringValue]];
490 [sharedSecretValue setStringValue:@""];
491 }
492
493 } else {
494 if (regSharedSecretValue) {
495 [sharedSecretValue setStringValue:regSharedSecretValue];
496 } else if ((keyName = [self sharedSecretKeyName:regDomainView.domain]) != NULL) {
497 [sharedSecretName setStringValue:keyName];
498 [sharedSecretValue setStringValue:@"****************"];
499 } else {
500 [sharedSecretName setStringValue:regDomainView.domain];
501 [sharedSecretValue setStringValue:@""];
502 }
503 }
504
505 [sharedSecretWindow resignFirstResponder];
506
507 if ([[sharedSecretName stringValue] length] > 0) [sharedSecretWindow makeFirstResponder:sharedSecretValue];
508 else [sharedSecretWindow makeFirstResponder:sharedSecretName];
509
510 [NSApp beginSheet:sharedSecretWindow modalForWindow:mainWindow modalDelegate:self
511 didEndSelector:@selector(sharedSecretSheetDidEnd:returnCode:contextInfo:) contextInfo:(__bridge void * _Null_unspecified)(sender)];
512 }
513
514
515 - (IBAction)selectWideAreaDomainButtonPressed:(id)sender
516 {
517 NSWindow * window = (([NSEvent modifierFlags] & NSAlternateKeyMask) == NSAlternateKeyMask) ? selectRegistrationDomainManualWindow : selectRegistrationDomainWindow;
518 regDomainTextField.stringValue = regDomainView.domain;
519
520 [self disableControls];
521 [NSApp beginSheet:window modalForWindow:mainWindow modalDelegate:self
522 didEndSelector:@selector(selectWideAreaDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:(__bridge void * _Null_unspecified)(sender)];
523
524 [self updateApplyButtonState];
525 }
526
527 - (void)selectWideAreaDomainSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
528 {
529 (void)contextInfo; // Unused
530 [sheet orderOut:self];
531 [self enableControls];
532
533 if (returnCode == NSOKButton) {
534 NSString * newRegDomainString;
535 if(sheet == selectRegistrationDomainManualWindow) newRegDomainString = [self trimCharactersFromDomain:[regDomainTextField stringValue]];
536 else newRegDomainString = [self trimCharactersFromDomain:registrationBrowserView.selectedDNSDomain];
537 regDomainView.domain = newRegDomainString;
538 [self updateApplyButtonState];
539 }
540 }
541
542 - (IBAction)wideAreaCheckBoxChanged:(id)sender
543 {
544 [self toggleWideAreaBonjour:[sender state]];
545 [self updateApplyButtonState];
546 [mainWindow makeFirstResponder:nil];
547 }
548
549
550
551 - (void)updateApplyButtonState
552 {
553 NSString *hostNameString = [hostName stringValue];
554 NSString *regDomainString = regDomainView.domain;
555 if ((currentHostName && ([hostNameString compare:currentHostName] != NSOrderedSame)) ||
556 (currentRegDomain && ([regDomainString compare:currentRegDomain] != NSOrderedSame) && ([wideAreaCheckBox state])) ||
557 (currentHostName == nil && ([hostNameString length]) > 0) ||
558 (currentRegDomain == nil && ([regDomainString length]) > 0) ||
559 (currentWideAreaState != [wideAreaCheckBox state]) ||
560 (hostNameSharedSecretValue != nil) ||
561 (regSharedSecretValue != nil) ||
562 (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO))
563 {
564 [self enableApplyButton];
565 } else {
566 [self disableApplyButton];
567 }
568 }
569
570
571 - (void)controlTextDidChange:(NSNotification *)notification
572 {
573 (void)notification; // Unused
574 [self updateApplyButtonState];
575 }
576
577
578 - (NSMutableArray *)registrationDataSource
579 {
580 return registrationDataSource;
581 }
582
583
584 - (NSString *)currentRegDomain
585 {
586 return currentRegDomain;
587 }
588
589
590 - (NSArray *)currentBrowseDomainsArray
591 {
592 return currentBrowseDomainsArray;
593 }
594
595
596 - (NSString *)currentHostName
597 {
598 return currentHostName;
599 }
600
601
602 - (NSString *)defaultRegDomain
603 {
604 return defaultRegDomain;
605 }
606
607
608 - (void)setDefaultRegDomain:(NSString *)domain
609 {
610 defaultRegDomain = domain;
611 }
612
613
614 - (void)didSelect
615 {
616 [super didSelect];
617 mainWindow = [[self mainView] window];
618 }
619
620 - (void)mainViewDidLoad
621 {
622 [comboAuthButton setString:"system.preferences"];
623 [comboAuthButton setDelegate:self];
624 [comboAuthButton setAutoupdate:YES];
625 [super mainViewDidLoad];
626 }
627
628
629
630 - (IBAction)applyClicked:(id)sender
631 {
632 (void)sender; // Unused
633 [self applyCurrentState];
634 }
635
636
637 - (void)applyCurrentState
638 {
639 [self validateTextFields];
640 [self savePreferences];
641 [self disableApplyButton];
642 [mainWindow makeFirstResponder:nil];
643 }
644
645
646 - (void)enableApplyButton
647 {
648 [applyButton setEnabled:YES];
649 [revertButton setEnabled:YES];
650 prefsNeedUpdating = YES;
651 }
652
653
654 - (void)disableApplyButton
655 {
656 [applyButton setEnabled:NO];
657 [revertButton setEnabled:NO];
658 prefsNeedUpdating = NO;
659 }
660
661
662 - (void)toggleWideAreaBonjour:(BOOL)state
663 {
664 [wideAreaCheckBox setState:state];
665 [registrationSelectButton setEnabled:state];
666 [registrationSharedSecretButton setEnabled:state];
667 }
668
669
670 - (IBAction)revertClicked:(id)sender
671 {
672 [self restorePreferences];
673 [browseDomainList deselectAll:sender];
674 [mainWindow makeFirstResponder:nil];
675 }
676
677
678 - (void)restorePreferences
679 {
680 [self setupInitialValues];
681 }
682
683
684 - (void)savePanelWillClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
685 {
686 (void)sheet; // Unused
687 DNSServiceDiscoveryPref * me = (__bridge DNSServiceDiscoveryPref *)contextInfo;
688
689 if (returnCode == NSAlertDefaultReturn) {
690 [me applyCurrentState];
691 } else if (returnCode == NSAlertAlternateReturn ) {
692 [me restorePreferences];
693 }
694
695 [me enableControls];
696 [me replyToShouldUnselect:(returnCode != NSAlertOtherReturn)];
697 }
698
699
700 -(SecKeychainItemRef)copyKeychainItemforDomain:(NSString *)domain
701 {
702 const char * serviceName = [domain UTF8String];
703 UInt32 type = 'ddns';
704 UInt32 typeLength = sizeof(type);
705
706 SecKeychainAttribute attrs[] = { { kSecServiceItemAttr, strlen(serviceName), (char *)serviceName },
707 { kSecTypeItemAttr, typeLength, (UInt32 *)&type } };
708
709 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
710 SecKeychainSearchRef searchRef;
711 SecKeychainItemRef itemRef = NULL;
712 OSStatus err;
713
714 err = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef);
715 if (err == noErr) {
716 err = SecKeychainSearchCopyNext(searchRef, &itemRef);
717 if (err != noErr) itemRef = NULL;
718 }
719 return itemRef;
720 }
721
722
723 -(NSString *)sharedSecretKeyName:(NSString * )domain
724 {
725 SecKeychainItemRef itemRef = NULL;
726 NSString *keyName = nil;
727 OSStatus err;
728
729 err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
730 assert(err == noErr);
731
732 itemRef = [self copyKeychainItemforDomain:[domain lowercaseString]];
733 if (itemRef) {
734 UInt32 tags[1];
735 SecKeychainAttributeInfo attrInfo;
736 SecKeychainAttributeList *attrList = NULL;
737 SecKeychainAttribute attribute;
738 unsigned int i;
739
740 tags[0] = kSecAccountItemAttr;
741 attrInfo.count = 1;
742 attrInfo.tag = tags;
743 attrInfo.format = NULL;
744
745 err = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, NULL, NULL);
746 if (err == noErr) {
747 for (i = 0; i < attrList->count; i++) {
748 attribute = attrList->attr[i];
749 if (attribute.tag == kSecAccountItemAttr) {
750 keyName = [[NSString alloc] initWithBytes:attribute.data length:attribute.length encoding:NSUTF8StringEncoding];
751 break;
752 }
753 }
754 if (attrList) (void)SecKeychainItemFreeAttributesAndData(attrList, NULL);
755 }
756 CFRelease(itemRef);
757 }
758 return keyName;
759 }
760
761
762 -(NSString *)domainForHostName:(NSString *)hostNameString
763 {
764 NSString * domainName = nil;
765 char text[64];
766 char * ptr = NULL;
767
768 ptr = (char *)[hostNameString UTF8String];
769 if (ptr) {
770 ptr = (char *)GetNextLabel(ptr, text);
771 domainName = [[NSString alloc] initWithUTF8String:(const char *)ptr];
772 }
773 return (domainName);
774 }
775
776
777 - (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled
778 {
779 NSMutableArray *domainsArray;
780 NSMutableDictionary *domainDict = nil;
781
782 if (domainName && [domainName length] > 0) {
783 domainDict= [NSMutableDictionary dictionaryWithCapacity:2];
784 [domainDict setObject:domainName forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
785 [domainDict setObject:[NSNumber numberWithBool:enabled] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
786 }
787 domainsArray = [NSMutableArray arrayWithCapacity:1];
788 if (domainDict) [domainsArray addObject:domainDict];
789 return [NSArchiver archivedDataWithRootObject:domainsArray];
790 }
791
792
793 - (NSData *)dataForDomainArray:(NSArray *)domainArray
794 {
795 return [NSArchiver archivedDataWithRootObject:domainArray];
796 }
797
798
799 - (NSDictionary *)dictionaryForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName
800 {
801 NSMutableDictionary *sharedSecretDict = [NSMutableDictionary dictionaryWithCapacity:3];
802 [sharedSecretDict setObject:secret forKey:(NSString *)SC_DYNDNS_SECRET_KEY];
803 [sharedSecretDict setObject:[domainName lowercaseString] forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
804 [sharedSecretDict setObject:keyName forKey:(NSString *)SC_DYNDNS_KEYNAME_KEY];
805 return sharedSecretDict;
806 }
807
808
809 -(void)savePreferences
810 {
811 NSString *hostNameString = [hostName stringValue];
812 NSString *regDomainString = regDomainView.domain;
813 NSString *tempHostNameSharedSecretName = hostNameSharedSecretName;
814 NSString *tempRegSharedSecretName = regSharedSecretName;
815 BOOL regSecretWasSet = NO;
816 BOOL hostSecretWasSet = NO;
817 BOOL updateHostname = NO;
818
819 hostNameString = [self trimCharactersFromDomain:hostNameString];
820 regDomainString = [self trimCharactersFromDomain:regDomainString];
821 tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName];
822 tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName];
823
824 [hostName setStringValue:hostNameString];
825 regDomainView.domain = regDomainString;
826
827 // Convert Shared Secret account names to lowercase.
828 tempHostNameSharedSecretName = [tempHostNameSharedSecretName lowercaseString];
829 tempRegSharedSecretName = [tempRegSharedSecretName lowercaseString];
830
831 // Save hostname shared secret.
832 if ([hostNameSharedSecretName length] > 0 && ([hostNameSharedSecretValue length] > 0)) {
833 DNSPrefTool_SetKeychainEntry([self dictionaryForSharedSecret:hostNameSharedSecretValue domain:hostNameString key:tempHostNameSharedSecretName]);
834 hostNameSharedSecretValue = nil;
835 hostSecretWasSet = YES;
836 }
837
838 // Save registration domain shared secret.
839 if (([regSharedSecretName length] > 0) && ([regSharedSecretValue length] > 0)) {
840 DNSPrefTool_SetKeychainEntry([self dictionaryForSharedSecret:regSharedSecretValue domain:regDomainString key:tempRegSharedSecretName]);
841 regSharedSecretValue = nil;
842 regSecretWasSet = YES;
843 }
844
845 // Save hostname.
846 if ((currentHostName == NULL) || [currentHostName compare:hostNameString] != NSOrderedSame) {
847 currentHostName = [hostNameString copy];
848 updateHostname = YES;
849 } else if (hostSecretWasSet) {
850 currentHostName = @"";
851 updateHostname = YES;
852 }
853
854 if (updateHostname) {
855 [BonjourSCStore setObject: currentHostName.length ? @[@{
856 (NSString *)SC_DYNDNS_DOMAIN_KEY : currentHostName,
857 (NSString *)SC_DYNDNS_ENABLED_KEY : @YES
858 }] : nil
859 forKey: (NSString *)SC_DYNDNS_HOSTNAMES_KEY];
860 }
861
862 // Save browse domain.
863 if (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
864 [BonjourSCStore setObject: browseDomainsArray forKey: (NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY];
865 currentBrowseDomainsArray = [browseDomainsArray copy];
866 }
867
868 // Save registration domain.
869 if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) {
870 [BonjourSCStore setObject: @[@{
871 (NSString *)SC_DYNDNS_DOMAIN_KEY : regDomainString,
872 (NSString *)SC_DYNDNS_ENABLED_KEY : [wideAreaCheckBox state] ? @YES : @NO
873 }]
874 forKey: (NSString *)SC_DYNDNS_REGDOMAINS_KEY];
875 currentRegDomain = [regDomainString copy];
876
877 if ([currentRegDomain length] > 0) {
878 currentWideAreaState = [wideAreaCheckBox state];
879 [registrationDataSource removeObject:regDomainString];
880 [registrationDataSource addObject:currentRegDomain];
881 [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil];
882 // [regDomainsComboBox reloadData];
883 } else {
884 currentWideAreaState = NO;
885 [self toggleWideAreaBonjour:NO];
886 if (defaultRegDomain != nil) regDomainView.domain = defaultRegDomain;
887 }
888 } else if (regSecretWasSet) {
889 [BonjourSCStore setObject: @[@{
890 (NSString *)SC_DYNDNS_DOMAIN_KEY : @"",
891 (NSString *)SC_DYNDNS_ENABLED_KEY : @NO
892 }]
893 forKey: (NSString *)SC_DYNDNS_REGDOMAINS_KEY];
894 if ([currentRegDomain length] > 0) {
895 [BonjourSCStore setObject: @[@{
896 (NSString *)SC_DYNDNS_DOMAIN_KEY : currentRegDomain,
897 (NSString *)SC_DYNDNS_ENABLED_KEY : currentWideAreaState ? @YES : @NO
898 }]
899 forKey: (NSString *)SC_DYNDNS_REGDOMAINS_KEY];
900 }
901 }
902 }
903
904
905 - (NSPreferencePaneUnselectReply)shouldUnselect
906 {
907 #if 1
908 if (prefsNeedUpdating == YES) {
909
910 [self disableControls];
911
912 NSBeginAlertSheet(
913 @"Apply Configuration Changes?",
914 @"Apply",
915 @"Don't Apply",
916 @"Cancel",
917 mainWindow,
918 self,
919 @selector( savePanelWillClose:returnCode:contextInfo: ),
920 NULL,
921 (__bridge void *) self, // sender,
922 @"" );
923 return NSUnselectLater;
924 }
925 #endif
926
927 return NSUnselectNow;
928 }
929
930
931 -(void)disableControls
932 {
933 [hostName setEnabled:NO];
934 [hostNameSharedSecretButton setEnabled:NO];
935 [applyButton setEnabled:NO];
936 [revertButton setEnabled:NO];
937 [wideAreaCheckBox setEnabled:NO];
938 [registrationSelectButton setEnabled: NO];
939 [registrationSharedSecretButton setEnabled:NO];
940 [statusImageView setEnabled:NO];
941
942 browseDomainListEnabled = NO;
943 [browseDomainList deselectAll:self];
944 [browseDomainList setEnabled:NO];
945
946 [addBrowseDomainButton setEnabled:NO];
947 [removeBrowseDomainButton setEnabled:NO];
948 }
949
950
951 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row
952 {
953 (void)row; // Unused
954 (void)tableView; // Unused
955 return browseDomainListEnabled;
956 }
957
958
959 -(void)enableControls
960 {
961 [hostName setEnabled:YES];
962 [hostNameSharedSecretButton setEnabled:YES];
963 [wideAreaCheckBox setEnabled:YES];
964 [registrationSelectButton setEnabled: YES];
965 [registrationSharedSecretButton setEnabled:YES];
966 [self toggleWideAreaBonjour:[wideAreaCheckBox state]];
967 [statusImageView setEnabled:YES];
968 [addBrowseDomainButton setEnabled:YES];
969
970 [browseDomainList setEnabled:YES];
971 [browseDomainList deselectAll:self];
972 browseDomainListEnabled = YES;
973
974 [removeBrowseDomainButton setEnabled:[browseDomainList numberOfSelectedRows]];
975 [applyButton setEnabled:prefsNeedUpdating];
976 [revertButton setEnabled:prefsNeedUpdating];
977 }
978
979
980 - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view
981 {
982 (void)view; // unused
983 [self enableControls];
984 }
985
986
987 - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view
988 {
989 (void)view; // unused
990 [self disableControls];
991 }
992
993 @end
994
995
996 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
997 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
998 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
999 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
1000 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
1001
1002 // NOT static -- otherwise the compiler may optimize it out
1003 // The "@(#) " pattern is a special prefix the "what" command looks for
1004 const char VersionString_SCCS[] = "@(#) Bonjour Preference Pane " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
1005
1006 #if _BUILDING_XCODE_PROJECT_
1007 // If the process crashes, then this string will be magically included in the automatically-generated crash log
1008 const char *__crashreporter_info__ = VersionString_SCCS + 5;
1009 asm(".desc ___crashreporter_info__, 0x10");
1010 #endif