Updating ignore patterns.
2 // MYBonjourRegistration.m
5 // Created by Jens Alfke on 4/27/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
9 #import "MYBonjourRegistration.h"
10 #import "MYBonjourService.h"
11 #import "ExceptionUtils.h"
17 #define kTXTTTL 60 // TTL in seconds for TXT records I register
20 @interface MYBonjourRegistration ()
21 @property BOOL registered;
25 @implementation MYBonjourRegistration
28 static NSMutableDictionary *sAllRegistrations;
31 + (void) priv_addRegistration: (MYBonjourRegistration*)reg {
32 if (!sAllRegistrations)
33 sAllRegistrations = [[NSMutableDictionary alloc] init];
34 [sAllRegistrations setObject: reg forKey: reg.fullName];
37 + (void) priv_removeRegistration: (MYBonjourRegistration*)reg {
38 [sAllRegistrations removeObjectForKey: reg.fullName];
41 + (MYBonjourRegistration*) registrationWithFullName: (NSString*)fullName {
42 return [sAllRegistrations objectForKey: fullName];
46 - (id) initWithServiceType: (NSString*)serviceType port: (UInt16)port
50 self.continuous = YES;
51 self.usePrivateConnection = YES; // DNSServiceUpdateRecord doesn't work with shared conn :(
52 _type = [serviceType copy];
67 @synthesize name=_name, type=_type, domain=_domain, port=_port, autoRename=_autoRename;
68 @synthesize registered=_registered;
71 - (NSString*) fullName {
72 return [[self class] fullNameOfService: _name ofType: _type inDomain: _domain];
76 - (BOOL) isSameAsService: (MYBonjourService*)service {
77 return _name && _domain && [self.fullName isEqualToString: service.fullName];
81 - (NSString*) description
83 return $sprintf(@"%@['%@'.%@%@]", self.class,_name,_type,_domain);
87 - (void) priv_registeredAsName: (NSString*)name
88 type: (NSString*)regtype
89 domain: (NSString*)domain
91 if (!$equal(name,_name))
93 if (!$equal(domain,_domain))
95 LogTo(Bonjour,@"Registered %@", self);
96 self.registered = YES;
97 [[self class] priv_addRegistration: self];
101 static void regCallback(DNSServiceRef sdRef,
102 DNSServiceFlags flags,
103 DNSServiceErrorType errorCode,
109 MYBonjourRegistration *reg = context;
112 [reg priv_registeredAsName: [NSString stringWithUTF8String: name]
113 type: [NSString stringWithUTF8String: regtype]
114 domain: [NSString stringWithUTF8String: domain]];
115 }catchAndReport(@"MYBonjourRegistration callback");
116 [reg gotResponse: errorCode];
120 - (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
121 DNSServiceFlags flags = 0;
123 flags |= kDNSServiceFlagsNoAutoRename;
124 NSData *txtData = nil;
126 txtData = [NSNetService dataFromTXTRecordDictionary: _txtRecord];
127 return DNSServiceRegister(sdRefPtr,
130 _name.UTF8String, // _name is likely to be nil
132 _domain.UTF8String, // _domain is most likely nil
145 [[self class] priv_removeRegistration: self];
146 self.registered = NO;
151 + (NSData*) dataFromTXTRecordDictionary: (NSDictionary*)txtDict {
152 // First translate any non-NSData values into UTF-8 formatted description data:
153 NSMutableDictionary *encodedDict = $mdict();
154 for (NSString *key in txtDict) {
155 id value = [txtDict objectForKey: key];
156 if (![value isKindOfClass: [NSData class]]) {
157 value = [[value description] dataUsingEncoding: NSUTF8StringEncoding];
159 [encodedDict setObject: value forKey: key];
161 return [NSNetService dataFromTXTRecordDictionary: encodedDict];
165 - (void) updateTxtRecord {
166 [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil];
167 if (self.serviceRef) {
168 NSData *data = [[self class] dataFromTXTRecordDictionary: _txtRecord];
169 Assert(data!=nil, @"Can't convert dictionary to TXT record");
170 DNSServiceErrorType err = DNSServiceUpdateRecord(self.serviceRef,
177 Warn(@"%@ failed to update TXT (err=%i)", self,err);
179 LogTo(Bonjour,@"%@ updated TXT to %@", self,data);
184 - (NSDictionary*) txtRecord {
188 - (void) setTxtRecord: (NSDictionary*)txtDict {
189 if (!$equal(_txtRecord,txtDict)) {
191 [_txtRecord setDictionary: txtDict];
193 setObj(&_txtRecord,nil);
194 [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil];
195 [self performSelector: @selector(updateTxtRecord) withObject: nil afterDelay: 0.1];
199 - (void) setString: (NSString*)value forTxtKey: (NSString*)key
201 NSData *data = [value dataUsingEncoding: NSUTF8StringEncoding];
202 if (!$equal(data, [_txtRecord objectForKey: key])) {
204 if (!_txtRecord) _txtRecord = [[NSMutableDictionary alloc] init];
205 [_txtRecord setObject: data forKey: key];
207 [_txtRecord removeObjectForKey: key];
208 [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil];
209 [self performSelector: @selector(updateTxtRecord) withObject: nil afterDelay: 0.1];
219 #pragma mark TESTING:
223 #import "MYBonjourQuery.h"
224 #import "MYAddressLookup.h"
226 @interface BonjourRegTester : NSObject
228 MYBonjourRegistration *_reg;
233 @implementation BonjourRegTester
236 NSDictionary *txt = $dict({@"time", $sprintf(@"%.3lf", CFAbsoluteTimeGetCurrent())});
237 _reg.txtRecord = txt;
238 [self performSelector: @selector(updateTXT) withObject: nil afterDelay: 3.0];
245 _reg = [[MYBonjourRegistration alloc] initWithServiceType: @"_foo._tcp" port: 12345];
246 [_reg addObserver: self forKeyPath: @"registered" options: NSKeyValueObservingOptionNew context: NULL];
247 [_reg addObserver: self forKeyPath: @"name" options: NSKeyValueObservingOptionNew context: NULL];
258 [_reg removeObserver: self forKeyPath: @"registered"];
259 [_reg removeObserver: self forKeyPath: @"name"];
264 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
266 LogTo(Bonjour,@"Observed change in %@: %@",keyPath,change);
271 TestCase(BonjourReg) {
272 EnableLogTo(Bonjour,YES);
273 EnableLogTo(DNS,YES);
274 [NSRunLoop currentRunLoop]; // create runloop
275 BonjourRegTester *tester = [[BonjourRegTester alloc] init];
276 [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 15]];
284 Copyright (c) 2008-2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
286 Redistribution and use in source and binary forms, with or without modification, are permitted
287 provided that the following conditions are met:
289 * Redistributions of source code must retain the above copyright notice, this list of conditions
290 and the following disclaimer.
291 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
292 and the following disclaimer in the documentation and/or other materials provided with the
295 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
296 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
297 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
298 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
299 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
300 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
301 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
302 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.