]> git.saurik.com Git - apple/mdnsresponder.git/blame - mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m
mDNSResponder-1310.80.1.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / PreferencePane / DNSServiceDiscoveryPref.m
CommitLineData
7cb34e5c
A
1/*
2 File: DNSServiceDiscoveryPref.m
3
4 Abstract: System Preference Pane for Dynamic DNS and Wide-Area DNS Service Discovery
5
9f221bca 6 Copyright: (c) Copyright 2005-2011 Apple Inc. All rights reserved.
7cb34e5c 7
9f221bca 8 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
7cb34e5c
A
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
9f221bca 22 Apple Inc. may be used to endorse or promote products derived from the
7cb34e5c
A
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.
263eeeab 42 */
7cb34e5c
A
43
44#import "DNSServiceDiscoveryPref.h"
12c5fa7a
A
45#import "CNDomainBrowserView.h"
46#import "BonjourSCStore.h"
47#import "BonjourPrefTool.h"
48#import <Foundation/NSXPCConnection_Private.h>
7cb34e5c 49
32bb7e43
A
50#include "../../Clients/ClientCommon.h"
51
12c5fa7a
A
52#pragma mark - BonjourPrefTool
53
54static OSStatus
55DNSPrefTool_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 }];
32bb7e43 74#endif
12c5fa7a
A
75 [prefTool setKeychainEntry: secretDictionary withStatus: ^(OSStatus status){
76 result = status;
77 }];
78
79 [_connectionToTool invalidate];
80
81 return (result);
82}
83
84#pragma mark -
32bb7e43 85
7cb34e5c
A
86@implementation DNSServiceDiscoveryPref
87
83fb1e36 88static NSInteger
7cb34e5c
A
89MyArrayCompareFunction(id val1, id val2, void *context)
90{
67c8f8a1 91 (void)context; // Unused
7cb34e5c
A
92 return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive);
93}
94
83fb1e36 95static NSInteger
7cb34e5c
A
96MyDomainArrayCompareFunction(id val1, id val2, void *context)
97{
67c8f8a1 98 (void)context; // Unused
7cb34e5c
A
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
7cb34e5c
A
105static void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
106{
67c8f8a1
A
107 (void)store; // Unused
108 (void)changedKeys; // Unused
12c5fa7a 109 DNSServiceDiscoveryPref * me = (__bridge DNSServiceDiscoveryPref *)context;
7cb34e5c
A
110 assert(me != NULL);
111
112 [me setupInitialValues];
113}
114
115
7cb34e5c
A
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{
12c5fa7a 127 SCDynamicStoreContext context = { 0, (__bridge void * _Nullable)(self), NULL, NULL, NULL };
7cb34e5c
A
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);
9f221bca 144 CFRelease(rls);
7cb34e5c
A
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
12c5fa7a 159 NSDictionary *dynamicDNS = (NSDictionary *)CFBridgingRelease(SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY));
7cb34e5c
A
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];
7cb34e5c
A
164 }
165 CFRelease(store);
166
167 return status;
168}
169
170
7cb34e5c
A
171-(void)readPreferences
172{
173 NSDictionary *origDict;
174 NSArray *regDomainArray;
175 NSArray *hostArray;
176
12c5fa7a
A
177 if (currentRegDomain) currentRegDomain = nil;
178 if (currentBrowseDomainsArray) currentBrowseDomainsArray = nil;
179 if (currentHostName) currentHostName = nil;
7cb34e5c
A
180
181 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL);
12c5fa7a 182 origDict = (NSDictionary *)CFBridgingRelease(SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY));
7cb34e5c
A
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 {
12c5fa7a 189 currentRegDomain = @"";
7cb34e5c
A
190 currentWideAreaState = NO;
191 }
192
12c5fa7a 193 currentBrowseDomainsArray = [origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY];
7cb34e5c
A
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 {
12c5fa7a 199 currentHostName = @"";
7cb34e5c
A
200 }
201
9f221bca 202 if (store) CFRelease(store);
7cb34e5c
A
203}
204
205
4a95efb2 206- (void)tableViewSelectionDidChange:(NSNotification *)notification
7cb34e5c
A
207{
208 [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]];
209}
210
211
4a95efb2 212- (IBAction)addBrowseDomainClicked:(id)sender
7cb34e5c 213{
12c5fa7a
A
214 NSWindow * window = (([NSEvent modifierFlags] & NSAlternateKeyMask) == NSAlternateKeyMask) ? addBrowseDomainManualWindow : addBrowseDomainWindow;
215 [browseDomainTextField setStringValue: [NSString string]];
7cb34e5c 216
12c5fa7a
A
217 [self disableControls];
218 [NSApp beginSheet:window modalForWindow:mainWindow modalDelegate:self
219 didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:(__bridge void * _Null_unspecified)(sender)];
7cb34e5c
A
220
221 [browseDomainList deselectAll:sender];
222 [self updateApplyButtonState];
223}
224
225
4a95efb2 226- (IBAction)removeBrowseDomainClicked:(id)sender
7cb34e5c 227{
67c8f8a1 228 (void)sender; // Unused
7cb34e5c
A
229 int selectedBrowseDomain = [browseDomainList selectedRow];
230 [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain];
231 [browseDomainList reloadData];
232 [self updateApplyButtonState];
233}
234
235
4a95efb2 236- (IBAction)enableBrowseDomainClicked:(id)sender
7cb34e5c
A
237{
238 NSTableView *tableView = sender;
239 NSMutableDictionary *browseDomainDict;
12c5fa7a 240 NSInteger value;
7cb34e5c
A
241
242 browseDomainDict = [[browseDomainsArray objectAtIndex:[tableView clickedRow]] mutableCopy];
243 value = [[browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue];
12c5fa7a 244 [browseDomainDict setObject:[NSNumber numberWithInt:(!value)] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
7cb34e5c
A
245 [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict];
246 [tableView reloadData];
247 [self updateApplyButtonState];
248}
249
250
251
4a95efb2 252- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
7cb34e5c 253{
67c8f8a1 254 (void)tableView; // Unused
7cb34e5c
A
255 int numberOfRows = 0;
256
257 if (browseDomainsArray) {
258 numberOfRows = [browseDomainsArray count];
259 }
260 return numberOfRows;
261}
262
263
4a95efb2 264- (void)tabView:(NSTabView *)xtabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
7cb34e5c 265{
67c8f8a1
A
266 (void)xtabView; // Unused
267 (void)tabViewItem; // Unused
7cb34e5c
A
268 [browseDomainList deselectAll:self];
269 [mainWindow makeFirstResponder:nil];
270}
271
272
4a95efb2 273- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
7cb34e5c 274{
67c8f8a1 275 (void)tableView; // Unused
7cb34e5c
A
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]) {
12c5fa7a 285 value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
7cb34e5c
A
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) {
7cb34e5c
A
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) {
12c5fa7a 311 [BonjourSCStore setObject: browseDomainsArray forKey: (NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY];
7cb34e5c
A
312 currentBrowseDomainsArray = [browseDomainsArray copy];
313 }
314 }
315 } else {
316 browseDomainsArray = nil;
317 }
318 [browseDomainList reloadData];
319
320 if (currentRegDomain && ([currentRegDomain length] > 0)) {
12c5fa7a 321 regDomainView.domain = currentRegDomain;
7cb34e5c
A
322 }
323
324 if (currentWideAreaState) {
325 [self toggleWideAreaBonjour:YES];
326 } else {
327 [self toggleWideAreaBonjour:NO];
328 }
329
330 if (hostNameSharedSecretValue) {
7cb34e5c
A
331 hostNameSharedSecretValue = nil;
332 }
333
334 if (regSharedSecretValue) {
7cb34e5c
A
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
12c5fa7a 347{
7cb34e5c 348 prefsNeedUpdating = NO;
7cb34e5c
A
349 browseDomainListEnabled = NO;
350 defaultRegDomain = nil;
351 currentRegDomain = nil;
352 currentBrowseDomainsArray = nil;
353 currentHostName = nil;
354 hostNameSharedSecretValue = nil;
355 regSharedSecretValue = nil;
356 browseDomainsArray = nil;
7cb34e5c
A
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];
7cb34e5c
A
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];
7cb34e5c 369 [self watchForPreferenceChanges];
7cb34e5c
A
370
371}
372
12c5fa7a
A
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}
7cb34e5c
A
387
388- (IBAction)closeMyCustomSheet:(id)sender
389{
12c5fa7a 390 BOOL result = [sender tag];
7cb34e5c
A
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{
12c5fa7a 399 NSButton * button = (__bridge NSButton *)contextInfo;
7cb34e5c
A
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];
67c8f8a1 423 while ((domainDict = [arrayEnumerator nextObject]) != NULL) {
7cb34e5c
A
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{
12c5fa7a 434 NSMutableCharacterSet * trimSet = [NSMutableCharacterSet whitespaceCharacterSet];
7cb34e5c
A
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{
67c8f8a1 442 (void)contextInfo; // Unused
7cb34e5c
A
443 [sheet orderOut:self];
444 [self enableControls];
445
446 if (returnCode == NSOKButton) {
12c5fa7a
A
447 NSString * newBrowseDomainString;
448 if(sheet == addBrowseDomainManualWindow) newBrowseDomainString = [self trimCharactersFromDomain:[browseDomainTextField stringValue]];
449 else newBrowseDomainString = [self trimCharactersFromDomain:bonjourBrowserView.selectedDNSDomain];
7cb34e5c 450 NSMutableDictionary *newBrowseDomainDict;
7cb34e5c
A
451 if (browseDomainsArray == nil) browseDomainsArray = [[NSMutableArray alloc] initWithCapacity:0];
452 if ([self domainAlreadyInList:newBrowseDomainString] == NO) {
12c5fa7a 453 newBrowseDomainDict = [[NSMutableDictionary alloc] initWithCapacity:2];
7cb34e5c
A
454
455 [newBrowseDomainDict setObject:newBrowseDomainString forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
12c5fa7a 456 [newBrowseDomainDict setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
7cb34e5c
A
457
458 [browseDomainsArray addObject:newBrowseDomainDict];
459 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
460 [browseDomainList reloadData];
461 [self updateApplyButtonState];
462 }
463 }
464}
465
7cb34e5c
A
466-(void)validateTextFields
467{
468 [hostName validateEditing];
12c5fa7a 469 [browseDomainTextField validateEditing];
7cb34e5c
A
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];
67c8f8a1 485 } else if ((keyName = [self sharedSecretKeyName:[hostName stringValue]]) != NULL) {
7cb34e5c
A
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];
12c5fa7a 496 } else if ((keyName = [self sharedSecretKeyName:regDomainView.domain]) != NULL) {
7cb34e5c
A
497 [sharedSecretName setStringValue:keyName];
498 [sharedSecretValue setStringValue:@"****************"];
499 } else {
12c5fa7a 500 [sharedSecretName setStringValue:regDomainView.domain];
7cb34e5c
A
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
12c5fa7a
A
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];
7cb34e5c
A
525}
526
12c5fa7a
A
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}
7cb34e5c
A
541
542- (IBAction)wideAreaCheckBoxChanged:(id)sender
543{
544 [self toggleWideAreaBonjour:[sender state]];
545 [self updateApplyButtonState];
546 [mainWindow makeFirstResponder:nil];
547}
548
549
12c5fa7a 550
7cb34e5c
A
551- (void)updateApplyButtonState
552{
553 NSString *hostNameString = [hostName stringValue];
12c5fa7a 554 NSString *regDomainString = regDomainView.domain;
9f221bca
A
555 if ((currentHostName && ([hostNameString compare:currentHostName] != NSOrderedSame)) ||
556 (currentRegDomain && ([regDomainString compare:currentRegDomain] != NSOrderedSame) && ([wideAreaCheckBox state])) ||
7cb34e5c
A
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
4a95efb2 571- (void)controlTextDidChange:(NSNotification *)notification
7cb34e5c 572{
67c8f8a1 573 (void)notification; // Unused
7cb34e5c
A
574 [self updateApplyButtonState];
575}
576
577
7cb34e5c
A
578- (NSMutableArray *)registrationDataSource
579{
580 return registrationDataSource;
581}
582
583
7cb34e5c
A
584- (NSString *)currentRegDomain
585{
586 return currentRegDomain;
587}
588
589
7cb34e5c
A
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{
7cb34e5c 610 defaultRegDomain = domain;
7cb34e5c
A
611}
612
613
614- (void)didSelect
615{
616 [super didSelect];
617 mainWindow = [[self mainView] window];
618}
619
7cb34e5c 620- (void)mainViewDidLoad
12c5fa7a 621{
7cb34e5c
A
622 [comboAuthButton setString:"system.preferences"];
623 [comboAuthButton setDelegate:self];
7cb34e5c 624 [comboAuthButton setAutoupdate:YES];
12c5fa7a 625 [super mainViewDidLoad];
7cb34e5c
A
626}
627
628
629
630- (IBAction)applyClicked:(id)sender
631{
67c8f8a1 632 (void)sender; // Unused
7cb34e5c
A
633 [self applyCurrentState];
634}
635
636
637- (void)applyCurrentState
638{
639 [self validateTextFields];
12c5fa7a
A
640 [self savePreferences];
641 [self disableApplyButton];
642 [mainWindow makeFirstResponder:nil];
7cb34e5c
A
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];
12c5fa7a 665 [registrationSelectButton setEnabled:state];
7cb34e5c
A
666 [registrationSharedSecretButton setEnabled:state];
667}
668
669
4a95efb2 670- (IBAction)revertClicked:(id)sender
7cb34e5c
A
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{
67c8f8a1 686 (void)sheet; // Unused
12c5fa7a 687 DNSServiceDiscoveryPref * me = (__bridge DNSServiceDiscoveryPref *)contextInfo;
7cb34e5c
A
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;
67c8f8a1 738 unsigned int i;
7cb34e5c
A
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 }
12c5fa7a 758 return keyName;
7cb34e5c
A
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 }
12c5fa7a 773 return (domainName);
7cb34e5c
A
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) {
12c5fa7a 783 domainDict= [NSMutableDictionary dictionaryWithCapacity:2];
7cb34e5c 784 [domainDict setObject:domainName forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
12c5fa7a 785 [domainDict setObject:[NSNumber numberWithBool:enabled] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
7cb34e5c 786 }
12c5fa7a 787 domainsArray = [NSMutableArray arrayWithCapacity:1];
7cb34e5c
A
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
12c5fa7a 799- (NSDictionary *)dictionaryForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName
7cb34e5c 800{
12c5fa7a 801 NSMutableDictionary *sharedSecretDict = [NSMutableDictionary dictionaryWithCapacity:3];
7cb34e5c
A
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];
12c5fa7a 805 return sharedSecretDict;
7cb34e5c
A
806}
807
808
809-(void)savePreferences
810{
811 NSString *hostNameString = [hostName stringValue];
12c5fa7a 812 NSString *regDomainString = regDomainView.domain;
7cb34e5c
A
813 NSString *tempHostNameSharedSecretName = hostNameSharedSecretName;
814 NSString *tempRegSharedSecretName = regSharedSecretName;
7cb34e5c
A
815 BOOL regSecretWasSet = NO;
816 BOOL hostSecretWasSet = NO;
12c5fa7a 817 BOOL updateHostname = NO;
7cb34e5c
A
818
819 hostNameString = [self trimCharactersFromDomain:hostNameString];
7cb34e5c
A
820 regDomainString = [self trimCharactersFromDomain:regDomainString];
821 tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName];
822 tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName];
823
824 [hostName setStringValue:hostNameString];
12c5fa7a 825 regDomainView.domain = regDomainString;
7cb34e5c
A
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)) {
12c5fa7a 833 DNSPrefTool_SetKeychainEntry([self dictionaryForSharedSecret:hostNameSharedSecretValue domain:hostNameString key:tempHostNameSharedSecretName]);
7cb34e5c
A
834 hostNameSharedSecretValue = nil;
835 hostSecretWasSet = YES;
836 }
837
838 // Save registration domain shared secret.
839 if (([regSharedSecretName length] > 0) && ([regSharedSecretValue length] > 0)) {
12c5fa7a 840 DNSPrefTool_SetKeychainEntry([self dictionaryForSharedSecret:regSharedSecretValue domain:regDomainString key:tempRegSharedSecretName]);
7cb34e5c
A
841 regSharedSecretValue = nil;
842 regSecretWasSet = YES;
843 }
844
845 // Save hostname.
846 if ((currentHostName == NULL) || [currentHostName compare:hostNameString] != NSOrderedSame) {
7cb34e5c 847 currentHostName = [hostNameString copy];
12c5fa7a 848 updateHostname = YES;
7cb34e5c 849 } else if (hostSecretWasSet) {
12c5fa7a
A
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];
7cb34e5c
A
860 }
861
862 // Save browse domain.
863 if (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) {
12c5fa7a 864 [BonjourSCStore setObject: browseDomainsArray forKey: (NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY];
7cb34e5c
A
865 currentBrowseDomainsArray = [browseDomainsArray copy];
866 }
867
868 // Save registration domain.
869 if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) {
12c5fa7a
A
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];
7cb34e5c
A
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];
12c5fa7a 882 // [regDomainsComboBox reloadData];
7cb34e5c
A
883 } else {
884 currentWideAreaState = NO;
885 [self toggleWideAreaBonjour:NO];
12c5fa7a 886 if (defaultRegDomain != nil) regDomainView.domain = defaultRegDomain;
7cb34e5c
A
887 }
888 } else if (regSecretWasSet) {
12c5fa7a
A
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 }
7cb34e5c
A
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,
12c5fa7a 921 (__bridge void *) self, // sender,
7cb34e5c
A
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];
7cb34e5c
A
935 [applyButton setEnabled:NO];
936 [revertButton setEnabled:NO];
937 [wideAreaCheckBox setEnabled:NO];
12c5fa7a 938 [registrationSelectButton setEnabled: NO];
7cb34e5c
A
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
4a95efb2 951- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row
7cb34e5c 952{
67c8f8a1
A
953 (void)row; // Unused
954 (void)tableView; // Unused
7cb34e5c
A
955 return browseDomainListEnabled;
956}
957
958
959-(void)enableControls
960{
961 [hostName setEnabled:YES];
962 [hostNameSharedSecretButton setEnabled:YES];
7cb34e5c 963 [wideAreaCheckBox setEnabled:YES];
12c5fa7a 964 [registrationSelectButton setEnabled: YES];
7cb34e5c
A
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{
12c5fa7a 982 (void)view; // unused
7cb34e5c
A
983 [self enableControls];
984}
985
986
987- (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view
988{
12c5fa7a 989 (void)view; // unused
7cb34e5c
A
990 [self disableControls];
991}
992
67c8f8a1
A
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
1004const char VersionString_SCCS[] = "@(#) Bonjour Preference Pane " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
c9d2d929 1005
32bb7e43 1006#if _BUILDING_XCODE_PROJECT_
67c8f8a1
A
1007// If the process crashes, then this string will be magically included in the automatically-generated crash log
1008const char *__crashreporter_info__ = VersionString_SCCS + 5;
1009asm(".desc ___crashreporter_info__, 0x10");
32bb7e43 1010#endif