]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m
mDNSResponder-107.3.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 Apple Computer, Inc. All rights reserved.
7
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.
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 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.
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 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"
47
48 Revision 1.5 2005/02/25 02:29:28 cheshire
49 Show yellow dot for "update in progress"
50
51 Revision 1.4 2005/02/16 00:18:33 cheshire
52 Bunch o' fixes
53
54 Revision 1.3 2005/02/10 22:35:20 cheshire
55 <rdar://problem/3727944> Update name
56
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.
60
61 Revision 1.1 2005/02/05 01:59:19 cheshire
62 Add Preference Pane to facilitate testing of DDNS & wide-area features
63
64 */
65
66 #import "DNSServiceDiscoveryPref.h"
67 #import "ConfigurationAuthority.h"
68 #import "PrivilegedOperations.h"
69 #import <unistd.h>
70
71 @implementation DNSServiceDiscoveryPref
72
73 static CFComparisonResult
74 MyArrayCompareFunction(id val1, id val2, void *context)
75 {
76 return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive);
77 }
78
79
80 static CFComparisonResult
81 MyDomainArrayCompareFunction(id val1, id val2, void *context)
82 {
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);
86 }
87
88
89 static const char *
90 GetNextLabel(const char *cstr, char label[64])
91 {
92 char *ptr = label;
93 while (*cstr && *cstr != '.') // While we have characters in the label...
94 {
95 char c = *cstr++;
96 if (c == '\\')
97 {
98 c = *cstr++;
99 if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1]))
100 {
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
106 }
107 }
108 *ptr++ = c;
109 if (ptr >= label+64) return(NULL);
110 }
111 if (*cstr) cstr++; // Skip over the trailing dot (if present)
112 *ptr++ = 0;
113 return(cstr);
114 }
115
116
117 static void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
118 {
119 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context;
120 assert(me != NULL);
121
122 [me setupInitialValues];
123 }
124
125
126 static void ServiceDomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
127 DNSServiceErrorType errorCode, const char *replyDomain, void *context, DNSServiceFlags enumType)
128 {
129 if (strcmp(replyDomain, "local.") == 0) return; // local domain is not interesting
130
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;
141
142 while (*buffer) {
143 buffer = (char *)GetNextLabel(buffer, nextLabel);
144 strcat(decodedDomainString, nextLabel);
145 strcat(decodedDomainString, ".");
146 }
147
148 // Remove trailing dot from domain name.
149 decodedDomainString[strlen(decodedDomainString)-1] = '\0';
150
151 domainString = [[[NSString alloc] initWithUTF8String:(const char *)decodedDomainString] autorelease];
152
153 if (enumType & kDNSServiceFlagsRegistrationDomains) {
154 domainArray = [me registrationDataSource];
155 domainComboBox = [me regDomainsComboBox];
156 currentDomain = [me currentRegDomain];
157 } else {
158 domainArray = [me browseDataSource];
159 domainComboBox = [me browseDomainsComboBox];
160 defaultBrowseDomainsArray = [me defaultBrowseDomainsArray];
161 }
162
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];
172 }
173 }
174
175 if (moreComing == NO) {
176 [domainArray sortUsingFunction:MyArrayCompareFunction context:nil];
177 [domainComboBox reloadData];
178 }
179 }
180
181
182 void
183 browseDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
184 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
185 {
186 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsBrowseDomains);
187 }
188
189
190 void
191 registrationDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
192 DNSServiceErrorType errorCode, const char *replyDomain, void *context)
193 {
194 ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsRegistrationDomains);
195 }
196
197
198
199 static void
200 MyDNSServiceCleanUp(MyDNSServiceState * query)
201 {
202 /* Remove the CFRunLoopSource from the current run loop. */
203 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
204 CFRelease(query->source);
205
206 /* Invalidate the CFSocket. */
207 CFSocketInvalidate(query->socket);
208 CFRelease(query->socket);
209
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> */
212 usleep(1000);
213
214 /* Terminate the connection with the mDNSResponder daemon, which cancels the query. */
215 DNSServiceRefDeallocate(query->service);
216 }
217
218
219
220 static void
221 MySocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void * data, void * info)
222 {
223 #pragma unused(s)
224 #pragma unused(type)
225 #pragma unused(address)
226 #pragma unused(data)
227
228 DNSServiceErrorType err;
229
230 MyDNSServiceState * query = (MyDNSServiceState *)info; // context passed in to CFSocketCreateWithNative().
231 assert(query != NULL);
232
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);
237
238 /* Terminate the query operation and release the CFRunLoopSource and CFSocket. */
239 MyDNSServiceCleanUp(query);
240 }
241 }
242
243
244
245 void
246 MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query)
247 {
248 CFSocketNativeHandle sock;
249 CFOptionFlags sockFlags;
250 CFSocketContext context = { 0, query, NULL, NULL, NULL }; // Use MyDNSServiceState as context data.
251
252 /* Access the underlying Unix domain socket to communicate with the mDNSResponder daemon. */
253 sock = DNSServiceRefSockFD(query->service);
254 assert(sock != -1);
255
256 /* Create a CFSocket using the Unix domain socket. */
257 query->socket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, MySocketReadCallback, &context);
258 assert(query->socket != NULL);
259
260 /* Prevent CFSocketInvalidate from closing DNSServiceRef's socket. */
261 sockFlags = CFSocketGetSocketFlags(query->socket);
262 CFSocketSetSocketFlags(query->socket, sockFlags & (~kCFSocketCloseOnInvalidate));
263
264 /* Create a CFRunLoopSource from the CFSocket. */
265 query->source = CFSocketCreateRunLoopSource(NULL, query->socket, 0);
266 assert(query->source != NULL);
267
268 /* Add the CFRunLoopSource to the current run loop. */
269 CFRunLoopAddSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes);
270 }
271
272
273
274 -(void)updateStatusImageView
275 {
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];
280 }
281
282
283 - (void)watchForPreferenceChanges
284 {
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;
289
290 assert(store != NULL);
291 assert(keys != NULL);
292
293 CFArrayAppendValue(keys, SC_DYNDNS_STATE_KEY);
294 CFArrayAppendValue(keys, SC_DYNDNS_SETUP_KEY);
295
296 (void)SCDynamicStoreSetNotificationKeys(store, keys, NULL);
297
298 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
299 assert(rls != NULL);
300
301 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
302
303 CFRelease(keys);
304 CFRelease(store);
305 }
306
307
308 -(int)statusForHostName:(NSString * )domain
309 {
310 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("statusForHostName"), NULL, NULL);
311 NSString *lowercaseDomain = [domain lowercaseString];
312 int status = 1;
313
314 assert(store != NULL);
315
316 NSDictionary *dynamicDNS = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY);
317 if (dynamicDNS) {
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);
322 }
323 CFRelease(store);
324
325 return status;
326 }
327
328
329 - (void)startDomainBrowsing
330 {
331 DNSServiceFlags flags;
332 OSStatus err = noErr;
333
334 flags = kDNSServiceFlagsRegistrationDomains;
335 err = DNSServiceEnumerateDomains(&regQuery.service, flags, 0, registrationDomainReply, (void *)self);
336 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(&regQuery);
337
338 flags = kDNSServiceFlagsBrowseDomains;
339 err = DNSServiceEnumerateDomains(&browseQuery.service, flags, 0, browseDomainReply, (void *)self);
340 if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(&browseQuery);
341 }
342
343
344 -(void)readPreferences
345 {
346 NSDictionary *origDict;
347 NSArray *regDomainArray;
348 NSArray *hostArray;
349
350 if (currentRegDomain) [currentRegDomain release];
351 if (currentBrowseDomainsArray) [currentBrowseDomainsArray release];
352 if (currentHostName) [currentHostName release];
353
354 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL);
355 origDict = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY);
356
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];
361 } else {
362 currentRegDomain = [[NSString alloc] initWithString:@""];
363 currentWideAreaState = NO;
364 }
365
366 currentBrowseDomainsArray = [[origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY] retain];
367
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];
371 } else {
372 currentHostName = [[NSString alloc] initWithString:@""];
373 }
374
375 [origDict release];
376 CFRelease(store);
377 }
378
379
380 - (void)tableViewSelectionDidChange:(NSNotification *)notification;
381 {
382 [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]];
383 }
384
385
386 - (void)setBrowseDomainsComboBox;
387 {
388 NSString * domain = nil;
389
390 if ([defaultBrowseDomainsArray count] > 0) {
391 NSEnumerator * arrayEnumerator = [defaultBrowseDomainsArray objectEnumerator];
392 while (domain = [arrayEnumerator nextObject]) {
393 if ([self domainAlreadyInList:domain] == NO) break;
394 }
395 }
396 if (domain) [browseDomainsComboBox setStringValue:domain];
397 else [browseDomainsComboBox setStringValue:@""];
398 }
399
400
401 - (IBAction)addBrowseDomainClicked:(id)sender;
402 {
403 [self setBrowseDomainsComboBox];
404
405 [NSApp beginSheet:addBrowseDomainWindow modalForWindow:mainWindow modalDelegate:self
406 didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
407
408 [browseDomainList deselectAll:sender];
409 [self updateApplyButtonState];
410 }
411
412
413 - (IBAction)removeBrowseDomainClicked:(id)sender;
414 {
415 int selectedBrowseDomain = [browseDomainList selectedRow];
416 [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain];
417 [browseDomainList reloadData];
418 [self updateApplyButtonState];
419 }
420
421
422 - (IBAction)enableBrowseDomainClicked:(id)sender;
423 {
424 NSTableView *tableView = sender;
425 NSMutableDictionary *browseDomainDict;
426 int value;
427
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];
434 }
435
436
437
438 - (int)numberOfRowsInTableView:(NSTableView *)tableView;
439 {
440 int numberOfRows = 0;
441
442 if (browseDomainsArray) {
443 numberOfRows = [browseDomainsArray count];
444 }
445 return numberOfRows;
446 }
447
448
449 - (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem;
450 {
451 [browseDomainList deselectAll:self];
452 [mainWindow makeFirstResponder:nil];
453 }
454
455
456 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row;
457 {
458 NSDictionary *browseDomainDict;
459 id value = nil;
460
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];
468 }
469 }
470 }
471 return value;
472 }
473
474
475 - (void)setupInitialValues
476 {
477 [self readPreferences];
478
479 if (currentHostName) {
480 [hostName setStringValue:currentHostName];
481 [self updateStatusImageView];
482 }
483
484 if (browseDomainsArray) {
485 [browseDomainsArray release];
486 browseDomainsArray = nil;
487 }
488
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];
498 }
499 }
500 } else {
501 browseDomainsArray = nil;
502 }
503 [browseDomainList reloadData];
504
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];
511 }
512
513 if (currentWideAreaState) {
514 [self toggleWideAreaBonjour:YES];
515 } else {
516 [self toggleWideAreaBonjour:NO];
517 }
518
519 if (hostNameSharedSecretValue) {
520 [hostNameSharedSecretValue release];
521 hostNameSharedSecretValue = nil;
522 }
523
524 if (regSharedSecretValue) {
525 [regSharedSecretValue release];
526 regSharedSecretValue = nil;
527 }
528
529 [self updateApplyButtonState];
530 [mainWindow makeFirstResponder:nil];
531 [browseDomainList deselectAll:self];
532 [removeBrowseDomainButton setEnabled:NO];
533 }
534
535
536
537 - (void)awakeFromNib
538 {
539 OSStatus err;
540
541 prefsNeedUpdating = NO;
542 toolInstalled = 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"];
556
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];
563
564 [tabView selectFirstTabViewItem:self];
565 [self setupInitialValues];
566 [self startDomainBrowsing];
567 [self watchForPreferenceChanges];
568
569 [tabView setDelegate:self];
570
571 InitConfigAuthority();
572 err = EnsureToolInstalled();
573 if (err == noErr) toolInstalled = YES;
574 else fprintf(stderr, "EnsureToolInstalled returned %ld\n", err);
575
576 }
577
578
579 - (IBAction)closeMyCustomSheet:(id)sender
580 {
581 BOOL result = [sender isEqualTo:browseOKButton] || [sender isEqualTo:secretOKButton];
582
583 if (result) [NSApp endSheet:[sender window] returnCode:NSOKButton];
584 else [NSApp endSheet:[sender window] returnCode:NSCancelButton];
585 }
586
587
588 - (void)sharedSecretSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
589 {
590 NSButton * button = (NSButton *)contextInfo;
591 [sheet orderOut:self];
592 [self enableControls];
593
594 if (returnCode == NSOKButton) {
595 if ([button isEqualTo:hostNameSharedSecretButton]) {
596 hostNameSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
597 hostNameSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
598 } else {
599 regSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]];
600 regSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]];
601 }
602 [self updateApplyButtonState];
603 }
604 [sharedSecretValue setStringValue:@""];
605 }
606
607
608 - (BOOL)domainAlreadyInList:(NSString *)domainString
609 {
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;
617 }
618 }
619 return NO;
620 }
621
622
623 - (NSString *)trimCharactersFromDomain:(NSString *)domain
624 {
625 NSMutableCharacterSet * trimSet = [[[NSCharacterSet whitespaceCharacterSet] mutableCopy] autorelease];
626 [trimSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
627 return [domain stringByTrimmingCharactersInSet:trimSet];
628 }
629
630
631 - (void)addBrowseDomainSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
632 {
633 [sheet orderOut:self];
634 [self enableControls];
635
636 if (returnCode == NSOKButton) {
637 NSString * newBrowseDomainString = [self trimCharactersFromDomain:[browseDomainsComboBox stringValue]];
638 NSMutableDictionary *newBrowseDomainDict;
639
640 if (browseDomainsArray == nil) browseDomainsArray = [[NSMutableArray alloc] initWithCapacity:0];
641 if ([self domainAlreadyInList:newBrowseDomainString] == NO) {
642 newBrowseDomainDict = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
643
644 [newBrowseDomainDict setObject:newBrowseDomainString forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY];
645 [newBrowseDomainDict setObject:[[[NSNumber alloc] initWithBool:YES] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY];
646
647 [browseDomainsArray addObject:newBrowseDomainDict];
648 [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil];
649 [browseDomainList reloadData];
650 [self updateApplyButtonState];
651 }
652 }
653 }
654
655
656 -(void)validateTextFields
657 {
658 [hostName validateEditing];
659 [browseDomainsComboBox validateEditing];
660 [regDomainsComboBox validateEditing];
661 }
662
663
664 - (IBAction)changeButtonPressed:(id)sender
665 {
666 NSString * keyName;
667
668 [self disableControls];
669 [self validateTextFields];
670 [mainWindow makeFirstResponder:nil];
671 [browseDomainList deselectAll:sender];
672
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:@"****************"];
679 } else {
680 [sharedSecretName setStringValue:[hostName stringValue]];
681 [sharedSecretValue setStringValue:@""];
682 }
683
684 } else {
685 if (regSharedSecretValue) {
686 [sharedSecretValue setStringValue:regSharedSecretValue];
687 } else if (keyName = [self sharedSecretKeyName:[regDomainsComboBox stringValue]]) {
688 [sharedSecretName setStringValue:keyName];
689 [sharedSecretValue setStringValue:@"****************"];
690 } else {
691 [sharedSecretName setStringValue:[regDomainsComboBox stringValue]];
692 [sharedSecretValue setStringValue:@""];
693 }
694 }
695
696 [sharedSecretWindow resignFirstResponder];
697
698 if ([[sharedSecretName stringValue] length] > 0) [sharedSecretWindow makeFirstResponder:sharedSecretValue];
699 else [sharedSecretWindow makeFirstResponder:sharedSecretName];
700
701 [NSApp beginSheet:sharedSecretWindow modalForWindow:mainWindow modalDelegate:self
702 didEndSelector:@selector(sharedSecretSheetDidEnd:returnCode:contextInfo:) contextInfo:sender];
703 }
704
705
706 - (IBAction)wideAreaCheckBoxChanged:(id)sender
707 {
708 [self toggleWideAreaBonjour:[sender state]];
709 [self updateApplyButtonState];
710 [mainWindow makeFirstResponder:nil];
711 }
712
713
714 - (void)updateApplyButtonState
715 {
716 NSString *hostNameString = [hostName stringValue];
717 NSString *regDomainString = [regDomainsComboBox stringValue];
718
719 NSComparisonResult hostNameResult = [hostNameString compare:currentHostName];
720 NSComparisonResult regDomainResult = [regDomainString compare:currentRegDomain];
721
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))
730 {
731 [self enableApplyButton];
732 } else {
733 [self disableApplyButton];
734 }
735 }
736
737
738
739 - (void)controlTextDidChange:(NSNotification *)notification;
740 {
741 [self updateApplyButtonState];
742 }
743
744
745
746 - (IBAction)comboAction:(id)sender;
747 {
748 [self updateApplyButtonState];
749 }
750
751
752 - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)index
753 {
754 NSString *domain = nil;
755 if ([aComboBox isEqualTo:browseDomainsComboBox]) domain = [browseDataSource objectAtIndex:index];
756 else if ([aComboBox isEqualTo:regDomainsComboBox]) domain = [registrationDataSource objectAtIndex:index];
757 return domain;
758 }
759
760
761
762 - (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox
763 {
764 int count = 0;
765 if ([aComboBox isEqualTo:browseDomainsComboBox]) count = [browseDataSource count];
766 else if ([aComboBox isEqualTo:regDomainsComboBox]) count = [registrationDataSource count];
767 return count;
768 }
769
770
771 - (NSMutableArray *)browseDataSource
772 {
773 return browseDataSource;
774 }
775
776
777 - (NSMutableArray *)registrationDataSource
778 {
779 return registrationDataSource;
780 }
781
782
783 - (NSComboBox *)browseDomainsComboBox
784 {
785 return browseDomainsComboBox;
786 }
787
788
789 - (NSComboBox *)regDomainsComboBox
790 {
791 return regDomainsComboBox;
792 }
793
794
795 - (NSString *)currentRegDomain
796 {
797 return currentRegDomain;
798 }
799
800
801 - (NSMutableArray *)defaultBrowseDomainsArray
802 {
803 return defaultBrowseDomainsArray;
804 }
805
806
807 - (NSArray *)currentBrowseDomainsArray
808 {
809 return currentBrowseDomainsArray;
810 }
811
812
813 - (NSString *)currentHostName
814 {
815 return currentHostName;
816 }
817
818
819 - (NSString *)defaultRegDomain
820 {
821 return defaultRegDomain;
822 }
823
824
825 - (void)setDefaultRegDomain:(NSString *)domain
826 {
827 [defaultRegDomain release];
828 defaultRegDomain = domain;
829 [defaultRegDomain retain];
830 }
831
832
833 - (void)didSelect
834 {
835 [super didSelect];
836 mainWindow = [[self mainView] window];
837 }
838
839
840 - (void)mainViewDidLoad
841 {
842 [comboAuthButton setString:"system.preferences"];
843 [comboAuthButton setDelegate:self];
844 [comboAuthButton updateStatus:nil];
845 [comboAuthButton setAutoupdate:YES];
846 }
847
848
849
850 - (IBAction)applyClicked:(id)sender
851 {
852 [self applyCurrentState];
853 }
854
855
856 - (void)applyCurrentState
857 {
858 [self validateTextFields];
859
860 if (toolInstalled == YES) {
861 [self savePreferences];
862 [self disableApplyButton];
863 [mainWindow makeFirstResponder:nil];
864 }
865 }
866
867
868 - (void)enableApplyButton
869 {
870 [applyButton setEnabled:YES];
871 [revertButton setEnabled:YES];
872 prefsNeedUpdating = YES;
873 }
874
875
876 - (void)disableApplyButton
877 {
878 [applyButton setEnabled:NO];
879 [revertButton setEnabled:NO];
880 prefsNeedUpdating = NO;
881 }
882
883
884 - (void)toggleWideAreaBonjour:(BOOL)state
885 {
886 [wideAreaCheckBox setState:state];
887 [regDomainsComboBox setEnabled:state];
888 [registrationSharedSecretButton setEnabled:state];
889 }
890
891
892 - (IBAction)revertClicked:(id)sender;
893 {
894 [self restorePreferences];
895 [browseDomainList deselectAll:sender];
896 [mainWindow makeFirstResponder:nil];
897 }
898
899
900 - (void)restorePreferences
901 {
902 [self setupInitialValues];
903 }
904
905
906 - (void)savePanelWillClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
907 {
908 DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)contextInfo;
909
910 if (returnCode == NSAlertDefaultReturn) {
911 [me applyCurrentState];
912 } else if (returnCode == NSAlertAlternateReturn ) {
913 [me restorePreferences];
914 }
915
916 [me enableControls];
917 [me replyToShouldUnselect:(returnCode != NSAlertOtherReturn)];
918 }
919
920
921 -(SecKeychainItemRef)copyKeychainItemforDomain:(NSString *)domain
922 {
923 const char * serviceName = [domain UTF8String];
924 UInt32 type = 'ddns';
925 UInt32 typeLength = sizeof(type);
926
927 SecKeychainAttribute attrs[] = { { kSecServiceItemAttr, strlen(serviceName), (char *)serviceName },
928 { kSecTypeItemAttr, typeLength, (UInt32 *)&type } };
929
930 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
931 SecKeychainSearchRef searchRef;
932 SecKeychainItemRef itemRef = NULL;
933 OSStatus err;
934
935 err = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef);
936 if (err == noErr) {
937 err = SecKeychainSearchCopyNext(searchRef, &itemRef);
938 if (err != noErr) itemRef = NULL;
939 }
940 return itemRef;
941 }
942
943
944 -(NSString *)sharedSecretKeyName:(NSString * )domain
945 {
946 SecKeychainItemRef itemRef = NULL;
947 NSString *keyName = nil;
948 OSStatus err;
949
950 err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
951 assert(err == noErr);
952
953 itemRef = [self copyKeychainItemforDomain:[domain lowercaseString]];
954 if (itemRef) {
955 UInt32 tags[1];
956 SecKeychainAttributeInfo attrInfo;
957 SecKeychainAttributeList *attrList = NULL;
958 SecKeychainAttribute attribute;
959 int i;
960
961 tags[0] = kSecAccountItemAttr;
962 attrInfo.count = 1;
963 attrInfo.tag = tags;
964 attrInfo.format = NULL;
965
966 err = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, NULL, NULL);
967 if (err == noErr) {
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];
972 break;
973 }
974 }
975 if (attrList) (void)SecKeychainItemFreeAttributesAndData(attrList, NULL);
976 }
977 CFRelease(itemRef);
978 }
979 return keyName;
980 }
981
982
983 -(NSString *)domainForHostName:(NSString *)hostNameString
984 {
985 NSString * domainName = nil;
986 char text[64];
987 char * ptr = NULL;
988
989 ptr = (char *)[hostNameString UTF8String];
990 if (ptr) {
991 ptr = (char *)GetNextLabel(ptr, text);
992 domainName = [[NSString alloc] initWithUTF8String:(const char *)ptr];
993 }
994 return ([domainName autorelease]);
995 }
996
997
998 - (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled
999 {
1000 NSMutableArray *domainsArray;
1001 NSMutableDictionary *domainDict = nil;
1002
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];
1007 }
1008 domainsArray = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
1009 if (domainDict) [domainsArray addObject:domainDict];
1010 return [NSArchiver archivedDataWithRootObject:domainsArray];
1011 }
1012
1013
1014 - (NSData *)dataForDomainArray:(NSArray *)domainArray
1015 {
1016 return [NSArchiver archivedDataWithRootObject:domainArray];
1017 }
1018
1019
1020 - (NSData *)dataForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName
1021 {
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];
1027 }
1028
1029
1030 -(void)savePreferences
1031 {
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;
1041
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];
1047
1048 [hostName setStringValue:hostNameString];
1049 [regDomainsComboBox setStringValue:regDomainString];
1050
1051 // Convert Shared Secret account names to lowercase.
1052 tempHostNameSharedSecretName = [tempHostNameSharedSecretName lowercaseString];
1053 tempRegSharedSecretName = [tempRegSharedSecretName lowercaseString];
1054
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;
1061 }
1062
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;
1069 }
1070
1071 // Save hostname.
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]);
1080 }
1081
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];
1088 }
1089
1090 // Save registration domain.
1091 if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) {
1092
1093 err = WriteRegistrationDomain((CFDataRef)[self dataForDomain:regDomainString isEnabled:[wideAreaCheckBox state]]);
1094 if (err != noErr) NSLog(@"WriteRegistrationDomain returned %d\n", err);
1095
1096 if (currentRegDomain) CFRelease(currentRegDomain);
1097 currentRegDomain = [regDomainString copy];
1098
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];
1105 } else {
1106 currentWideAreaState = NO;
1107 [self toggleWideAreaBonjour:NO];
1108 if (defaultRegDomain != nil) [regDomainsComboBox setStringValue:defaultRegDomain];
1109 }
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]);
1114 }
1115 }
1116
1117
1118 - (NSPreferencePaneUnselectReply)shouldUnselect
1119 {
1120 #if 1
1121 if (prefsNeedUpdating == YES) {
1122
1123 [self disableControls];
1124
1125 NSBeginAlertSheet(
1126 @"Apply Configuration Changes?",
1127 @"Apply",
1128 @"Don't Apply",
1129 @"Cancel",
1130 mainWindow,
1131 self,
1132 @selector( savePanelWillClose:returnCode:contextInfo: ),
1133 NULL,
1134 (void *) self, // sender,
1135 @"" );
1136 return NSUnselectLater;
1137 }
1138 #endif
1139
1140 return NSUnselectNow;
1141 }
1142
1143
1144 -(void)disableControls
1145 {
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];
1155
1156 browseDomainListEnabled = NO;
1157 [browseDomainList deselectAll:self];
1158 [browseDomainList setEnabled:NO];
1159
1160 [addBrowseDomainButton setEnabled:NO];
1161 [removeBrowseDomainButton setEnabled:NO];
1162 }
1163
1164
1165 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
1166 {
1167 return browseDomainListEnabled;
1168 }
1169
1170
1171 -(void)enableControls
1172 {
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];
1181
1182 [browseDomainList setEnabled:YES];
1183 [browseDomainList deselectAll:self];
1184 browseDomainListEnabled = YES;
1185
1186 [removeBrowseDomainButton setEnabled:[browseDomainList numberOfSelectedRows]];
1187 [applyButton setEnabled:prefsNeedUpdating];
1188 [revertButton setEnabled:prefsNeedUpdating];
1189 }
1190
1191
1192 - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view
1193 {
1194 [self enableControls];
1195 }
1196
1197
1198 - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view
1199 {
1200 [self disableControls];
1201 }
1202
1203
1204 @end