Plugins/Bonjour/libezv/Private Classes/AWEzvContactManagerRendezvous.m
author Evan Schoenberg
Sat Oct 31 12:31:33 2009 -0500 (2009-10-31)
changeset 2706 13898cc883cd
parent 2093 d470a05e220b
permissions -rw-r--r--
If Bonjour disconnects but the user opens a contact window in the moment before the contact disappears from the list, it wass possible to attempt to open with a nil avInstanceName. Furthermore, if Bonjour disconnects, attempting to send a message to a still-open Bonjour chat could try to reference avInstanceName which would previously be nil after disconnect.

There's no reason to be so aggressive with having avInstanceName only be set while logged in; it's a single string, and it doesn't generally change. We'll create it in init and destroy it in dealloc for AWEzvContactManager.

Fixes #11799 fully.
David@0
     1
/*
David@0
     2
 * Project:     Libezv
David@0
     3
 * File:        AWEzvContactManagerRendezvous.m
David@0
     4
 *
David@0
     5
 * Version:     1.0
David@0
     6
 * Author:      Andrew Wellington <proton[at]wiretapped.net>
David@0
     7
 *
David@0
     8
 * License:
David@0
     9
 * Copyright (C) 2004-2007 Andrew Wellington.
David@0
    10
 * All rights reserved.
David@0
    11
 * 
David@0
    12
 * Redistribution and use in source and binary forms, with or without
David@0
    13
 * modification, are permitted provided that the following conditions are met:
David@0
    14
 * 
David@0
    15
 * 1. Redistributions of source code must retain the above copyright notice,
David@0
    16
 * this list of conditions and the following disclaimer.
David@0
    17
 * 2. Redistributions in binary form must reproduce the above copyright notice,
David@0
    18
 * this list of conditions and the following disclaimer in the documentation
David@0
    19
 * and/or other materials provided with the distribution.
David@0
    20
 * 
David@0
    21
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
David@0
    22
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
David@0
    23
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
David@0
    24
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
David@0
    25
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
David@0
    26
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
David@0
    27
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
David@0
    28
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
David@0
    29
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
David@0
    30
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
David@0
    31
 */
David@0
    32
David@0
    33
#import "AWEzvContactManager.h"
David@0
    34
#import "AWEzvContactManagerRendezvous.h"
David@0
    35
#import "AWEzv.h"
David@0
    36
#import "AWEzvPrivate.h"
David@0
    37
#import "AWEzvContact.h"
David@0
    38
#import "AWEzvContactPrivate.h"
David@0
    39
#import "AWEzvRendezvousData.h"
David@0
    40
David@0
    41
#import "AWEzvSupportRoutines.h"
David@0
    42
catfish@2093
    43
#import <dns_sd.h>
David@0
    44
catfish@2093
    45
#import <openssl/sha.h>
David@0
    46
David@0
    47
/* One of the stupidest things I've ever met. Doing DNS lookups using the standard
David@0
    48
 * functions does not for mDNS records work unless you're in BIND 8 compatibility
David@0
    49
 * mode. And of course how do you get data from say a NULL record for iChat stuff?
David@0
    50
 * With the standard DNS functions. So we have to use BIND 8 mode. Which means we
David@0
    51
 * have to implement our own DNS packet parser. What were people thinking here?
David@0
    52
 */
David@0
    53
#define BIND_8_COMPAT 1
catfish@2093
    54
#import <sys/types.h>
catfish@2093
    55
#import <sys/socket.h>
catfish@2093
    56
#import <netinet/in.h>
catfish@2093
    57
#import <arpa/nameser.h>
catfish@2093
    58
#import <arpa/inet.h>
catfish@2093
    59
#import <netdb.h>
catfish@2093
    60
#import <resolv.h>
catfish@2093
    61
#import <errno.h>
catfish@2093
    62
#import <ctype.h>
catfish@2093
    63
#import <string.h>
catfish@2093
    64
#import <stdlib.h>
David@0
    65
catfish@2093
    66
#import <SystemConfiguration/SystemConfiguration.h>
David@0
    67
// The ServiceController manages cleanup of DNSServiceRef & runloop info for an outstanding request
David@0
    68
@interface ServiceController : NSObject
David@0
    69
{
David@0
    70
	DNSServiceRef			fServiceRef;
David@0
    71
	CFSocketRef				fSocketRef;
David@0
    72
	CFRunLoopSourceRef		fRunloopSrc;
David@0
    73
	AWEzvContactManager		*contactManager;
David@0
    74
}
David@0
    75
David@0
    76
- (id)initWithServiceRef:(DNSServiceRef)ref forContactManager:(AWEzvContactManager *)inContactManager;
David@0
    77
- (boolean_t)addToCurrentRunLoop;
David@0
    78
- (void)breakdownServiceController;
David@0
    79
- (DNSServiceRef)serviceRef;
David@561
    80
@property (readonly, nonatomic) AWEzvContactManager *contactManager;
David@0
    81
David@0
    82
@end // interface ServiceController
David@0
    83
David@0
    84
/* C-helper function prototypes */
David@0
    85
void register_reply ( 
David@0
    86
    DNSServiceRef sdRef, 
David@0
    87
    DNSServiceFlags flags, 
David@0
    88
    DNSServiceErrorType errorCode, 
David@0
    89
    const char *name, 
David@0
    90
    const char *regtype, 
David@0
    91
    const char *domain, 
David@0
    92
    void *context );
David@0
    93
David@0
    94
static void ProcessSockData (
David@0
    95
   CFSocketRef s,
David@0
    96
   CFSocketCallBackType callbackType,
David@0
    97
   CFDataRef address,
David@0
    98
   const void *data,
David@0
    99
   void *info
David@0
   100
);
David@0
   101
David@0
   102
void handle_av_browse_reply ( 
David@0
   103
    DNSServiceRef sdRef, 
David@0
   104
    DNSServiceFlags flags, 
David@0
   105
    uint32_t interfaceIndex, 
David@0
   106
    DNSServiceErrorType errorCode, 
David@0
   107
    const char *serviceName, 
David@0
   108
    const char *regtype, 
David@0
   109
    const char *replyDomain, 
David@0
   110
    void *context );
David@0
   111
David@0
   112
void resolve_reply ( 
David@0
   113
    DNSServiceRef sdRef, 
David@0
   114
    DNSServiceFlags flags, 
David@0
   115
    uint32_t interfaceIndex, 
David@0
   116
    DNSServiceErrorType errorCode, 
David@0
   117
    const char *fullname, 
David@0
   118
    const char *hosttarget, 
David@0
   119
    uint16_t port, 
David@0
   120
    uint16_t txtLen, 
David@571
   121
    const unsigned char *txtRecord, 
David@0
   122
    void *context );
David@0
   123
David@0
   124
void AddressQueryRecordReply( DNSServiceRef DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, 
David@0
   125
								DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, 
David@0
   126
								uint16_t rdlen, const void *rdata, uint32_t ttl, void *context );
David@0
   127
								
David@0
   128
void ImageQueryRecordReply( DNSServiceRef DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, 
David@0
   129
								DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, 
David@0
   130
								uint16_t rdlen, const void *rdata, uint32_t ttl, void *context );	
David@0
   131
								
David@0
   132
void image_register_reply ( 
David@0
   133
    DNSServiceRef sdRef, 
David@0
   134
    DNSRecordRef RecordRef, 
David@0
   135
    DNSServiceFlags flags, 
David@0
   136
    DNSServiceErrorType errorCode, 
David@0
   137
    void *context );							
David@0
   138
David@0
   139
@implementation AWEzvContactManager (Rendezvous)
David@0
   140
#pragma mark Announcing Functions
David@0
   141
- (void) login {
David@0
   142
	regCount = 0;
David@0
   143
David@0
   144
	/* create data structure we'll advertise with */
David@0
   145
	userAnnounceData = [[AWEzvRendezvousData alloc] init];
David@0
   146
	/* set field contents of the data */
David@0
   147
	[userAnnounceData setField:@"1st" content:[client name]];
David@0
   148
	[userAnnounceData setField:@"email" content:@""];
David@0
   149
	[userAnnounceData setField:@"ext" content:@""];
David@0
   150
	[userAnnounceData setField:@"jid" content:@""];
David@0
   151
	[userAnnounceData setField:@"last" content:@""];
David@0
   152
	[userAnnounceData setField:@"msg" content:@""];
David@0
   153
	[userAnnounceData setField:@"nick" content:@""];
David@0
   154
	[userAnnounceData setField:@"node" content:@""];
David@0
   155
	[userAnnounceData setField:@"AIM" content:@""];
David@0
   156
	[userAnnounceData setField:@"email" content:@""];
David@0
   157
	[userAnnounceData setField:@"port.p2pj" content:[NSString stringWithFormat:@"%u", port]];
David@0
   158
	[userAnnounceData setField:@"txtvers" content:@"1"];
David@0
   159
	[userAnnounceData setField:@"version" content:@"1"];
David@0
   160
David@0
   161
	[self setStatus:[client status] withMessage:nil];
Evan@2706
   162
	
David@0
   163
    /* register service with mDNSResponder */
David@0
   164
David@0
   165
	DNSServiceRef servRef;
David@0
   166
	DNSServiceErrorType dnsError;
David@0
   167
David@0
   168
	TXTRecordRef txtRecord;
David@0
   169
	txtRecord = [userAnnounceData dataAsTXTRecordRef];
David@0
   170
David@0
   171
	dnsError = DNSServiceRegister(
David@0
   172
			/* uninitialized service discovery reference */ &servRef, 
Evan@663
   173
		    /* flags indicating how to handle name conflicts */ /* kDNSServiceFlagsNoAutoRename */ 0, 
David@0
   174
		    /* interface on which to register, 0 for all available */ 0, 
David@0
   175
		    /* service's name, may be null */ [avInstanceName UTF8String],
David@0
   176
		    /* service registration type */ "_presence._tcp", 
David@0
   177
		    /* domain, may be NULL */ NULL,
David@0
   178
		    /* SRV target host name, may be NULL */ NULL,
David@0
   179
		    /* port number in network byte order */ htons(port), 
David@0
   180
		    /* length of txt record in bytes, 0 for NULL txt record */ TXTRecordGetLength(&txtRecord) , 
David@0
   181
		    /* txt record properly formatted, may be NULL */ TXTRecordGetBytesPtr(&txtRecord) ,
David@0
   182
		    /* call back function, may be NULL */ register_reply,
David@0
   183
			/* application context pointer, may be null */ self);
David@0
   184
David@0
   185
	if (dnsError == kDNSServiceErr_NoError) {		
David@0
   186
		fDomainBrowser = [[ServiceController alloc] initWithServiceRef:servRef forContactManager:self];
David@0
   187
		[fDomainBrowser addToCurrentRunLoop];
David@0
   188
		avDNSReference = servRef;
David@0
   189
	} else {
David@0
   190
		[[client client] reportError:@"Could not register DNS service: _presence._tcp" ofLevel:AWEzvConnectionError];
David@0
   191
		[self disconnect];
David@0
   192
	}
David@407
   193
	TXTRecordDeallocate(&txtRecord);
David@0
   194
}
David@0
   195
David@0
   196
/* this is used for a clean logout */
David@0
   197
- (void) logout {
David@0
   198
    [self disconnect];
David@0
   199
}
David@0
   200
David@0
   201
/* this causes an actual disconnect */
David@0
   202
- (void) disconnect {
David@0
   203
	AILogWithSignature(@"Disconnecting");
David@0
   204
David@0
   205
	[fServiceBrowser release]; fServiceBrowser = nil;
David@0
   206
David@0
   207
	/* Remove Resolvers, this also deallocates the DNSServiceReferences */
David@0
   208
	if (fDomainBrowser != nil) {
David@0
   209
		AILogWithSignature(@"Releasing %@",fDomainBrowser);
David@0
   210
		[fDomainBrowser release]; fDomainBrowser = nil;
David@0
   211
David@0
   212
		avDNSReference = nil;
David@0
   213
		imageServiceRef = nil;
David@0
   214
	}
David@0
   215
David@0
   216
	[self setConnected:NO];
David@0
   217
}
David@0
   218
David@0
   219
- (void) setConnected:(BOOL)connected {
David@0
   220
	if (isConnected != connected) {
David@0
   221
		isConnected = connected;
David@0
   222
		if (connected)
David@0
   223
			[[client client] reportLoggedIn];
David@0
   224
		else
David@0
   225
			[[client client] reportLoggedOut];
David@0
   226
	}
David@0
   227
}
David@0
   228
David@0
   229
- (void)setStatus:(AWEzvStatus)status withMessage:(NSString *)message {
David@0
   230
	NSString *statusString; /* string for use in Rendezous field */
David@0
   231
	/* work out the string for rendezvous */
David@0
   232
	switch (status) {
David@0
   233
		case AWEzvIdle:
David@0
   234
			statusString = @"away";
David@0
   235
	    	break;
David@0
   236
		case AWEzvAway:
David@0
   237
			statusString = @"dnd";
David@0
   238
			break;
David@0
   239
		case AWEzvOnline:
David@0
   240
			statusString = @"avail";
David@0
   241
			break;
David@0
   242
		default:
David@0
   243
	    	/* if something weird, default to available */
David@0
   244
			statusString = @"avail";
David@0
   245
	}
David@0
   246
David@0
   247
	/* add it to our data */
David@0
   248
	[userAnnounceData setField:@"status" content:statusString];
David@0
   249
David@0
   250
	/* now set the message */
David@0
   251
	if ([message length]) {
David@0
   252
		[userAnnounceData setField:@"msg" content:message];
David@0
   253
	} else {
David@0
   254
		[userAnnounceData deleteField:@"msg"];
David@0
   255
	}
David@0
   256
David@0
   257
	/* check for idle */
David@0
   258
	if ([client idleTime]) {
David@0
   259
		[userAnnounceData setField:@"away" content:[NSString stringWithFormat:@"%f", [[client idleTime] timeIntervalSinceReferenceDate]]];
David@0
   260
	} else {
David@0
   261
		[userAnnounceData deleteField:@"away"];
David@0
   262
	}
David@0
   263
David@0
   264
	/* announce to network */
David@0
   265
	if (isConnected == YES)
David@0
   266
		[self updateAnnounceInfo];
David@0
   267
}
David@0
   268
David@0
   269
/* udpates information announced over network for user */
David@0
   270
- (void) updateAnnounceInfo {
David@0
   271
	DNSServiceErrorType updateError;
David@0
   272
	TXTRecordRef txtRecord;
David@0
   273
David@0
   274
	if (!isConnected)
David@0
   275
		return;
David@0
   276
David@0
   277
	if (avDNSReference == NULL) {
David@0
   278
		[[client client] reportError:@"avDNSReference is null when trying to update the TXT Record" ofLevel:AWEzvWarning];
David@0
   279
		return;
David@0
   280
	}
David@0
   281
David@0
   282
	txtRecord = [userAnnounceData dataAsTXTRecordRef];
Evan@663
   283
	AILogWithSignature(@"%@", [userAnnounceData dictionary]);
David@0
   284
	updateError = DNSServiceUpdateRecord (
David@0
   285
		/* serviceRef */ avDNSReference,
David@0
   286
		/* recordRef, may be NULL */ NULL,
David@0
   287
		/* Flags, currently ignored */ 0,
David@0
   288
		/* length */ TXTRecordGetLength(&txtRecord),
David@0
   289
		/* data */ TXTRecordGetBytesPtr(&txtRecord),
David@0
   290
		/* time to live */ 0 );
David@0
   291
	if (updateError != kDNSServiceErr_NoError) {		
David@0
   292
		[[client client] reportError:@"Error updating TXT Record" ofLevel:AWEzvConnectionError];
David@0
   293
		[self disconnect];
David@0
   294
	}
David@407
   295
	
David@407
   296
	TXTRecordDeallocate(&txtRecord);
David@0
   297
}
David@0
   298
David@0
   299
- (void) updatedName {
David@0
   300
	[userAnnounceData setField:@"1st" content:[client name]];
David@0
   301
	[self updateAnnounceInfo];
David@0
   302
}
David@0
   303
David@0
   304
- (void) updatedStatus {
David@0
   305
	[self setStatus:[client status] withMessage:[userAnnounceData getField:@"msg"]];
David@0
   306
}
David@0
   307
David@0
   308
- (void)setImageData:(NSData *)JPEGData {
David@0
   309
	DNSServiceErrorType error;
David@568
   310
	SHA_CTX ctx;
David@0
   311
	unsigned char digest[20];
David@0
   312
David@0
   313
	if (avDNSReference == NULL) {
David@0
   314
		[[client client] reportError:@"Error setting image data" ofLevel:AWEzvWarning];
David@0
   315
		return;
David@0
   316
	}
David@0
   317
David@0
   318
	if (JPEGData == nil) {
David@0
   319
		/* no image so remove record and update txt records*/
David@0
   320
		error = DNSServiceRemoveRecord ( 
David@0
   321
		    /* service reference */ avDNSReference, 
David@0
   322
		    /* record reference */ imageRef, 
David@0
   323
		    /* flags, ignored */ 0);
David@0
   324
		if (error == kDNSServiceErr_NoError) {
David@0
   325
			imageRef = nil;
David@0
   326
			[userAnnounceData deleteField:@"phsh"];
David@0
   327
			[self updateAnnounceInfo];
David@0
   328
			return;
David@0
   329
		}
David@0
   330
	}
David@0
   331
David@0
   332
	if (imageRef != nil && JPEGData != nil) {
David@0
   333
		/* Remove the old reference before updating the image. 
David@0
   334
		 * This works around a bug experienced when updating the record to use an image that occupied more space
David@0
   335
		 **/
David@0
   336
		error = DNSServiceRemoveRecord ( 
David@0
   337
		    /* service reference */ /*imageServiceRef*/avDNSReference, 
David@0
   338
		    /* record reference */ imageRef, 
David@0
   339
			/* flags, ignored */ 1);
David@0
   340
		if (error != kDNSServiceErr_NoError) {
David@0
   341
			[[client client] reportError:@"Error removing old image before setting new image" ofLevel:AWEzvWarning];
David@0
   342
			return;
David@0
   343
		} else {
David@0
   344
			[userAnnounceData deleteField:@"phsh"];
David@0
   345
			imageRef = nil;
David@0
   346
		}
David@0
   347
	}
David@0
   348
David@0
   349
	error = DNSServiceAddRecord (/*service reference */ avDNSReference, 
David@0
   350
	                             /*record reference */ &imageRef, 
David@0
   351
	                             /* flags, ignored */ 0, 
David@0
   352
	                             /* type */ kDNSServiceType_NULL, 
David@0
   353
	                             /* length */ [JPEGData length], 
David@0
   354
	                             /* data */ [JPEGData bytes], 
Evan@663
   355
	                             /* time to live; 0=default */ 0);
David@0
   356
David@0
   357
	if (error == kDNSServiceErr_NoError) {
David@0
   358
		/* Let's create the hash */
David@568
   359
		SHA1_Init(&ctx);
David@568
   360
		SHA1_Update(&ctx, [JPEGData bytes], (unsigned long)[JPEGData length]);
David@568
   361
		SHA1_Final(digest, &ctx);
David@0
   362
		imagehash = [[NSData dataWithBytes:digest length:20] retain];
Evan@663
   363
		AILogWithSignature(@"Will update with hash %@; length is %u", imagehash, [JPEGData length]);
David@0
   364
		[self updatePHSH];
David@0
   365
	} else {
David@0
   366
		[[client client] reportError:@"Error adding image record" ofLevel:AWEzvWarning];
David@0
   367
	}
David@0
   368
}
David@0
   369
David@0
   370
- (void) updatePHSH {
David@0
   371
	if (imagehash != nil) {
David@407
   372
		[userAnnounceData setField:@"phsh" content:[imagehash autorelease]];
David@0
   373
		/* announce to network */
David@0
   374
		[self updateAnnounceInfo];
David@0
   375
	} else {
David@0
   376
		[userAnnounceData deleteField:@"phsh"];
David@0
   377
	}
David@0
   378
}
David@0
   379
David@0
   380
#pragma mark Browsing Functions
David@0
   381
/* start browsing the network for new rendezvous clients */
David@0
   382
- (void) startBrowsing {
David@0
   383
	[fServiceBrowser release]; fServiceBrowser = nil;
David@0
   384
David@0
   385
	/* destroy old contact dictionary if one exists */
David@0
   386
	[contacts release];
David@0
   387
David@0
   388
	/* allocate new contact dictionary */
David@0
   389
	contacts = [[NSMutableDictionary alloc] init];
David@0
   390
David@0
   391
	/* create AV browser */
David@0
   392
	DNSServiceRef browsRef;
David@0
   393
	DNSServiceErrorType avBrowseError;
David@0
   394
David@0
   395
	avBrowseError = DNSServiceBrowse (/* uninitialized DNSServiceRef */ &browsRef,
David@0
   396
	                                  /* flags, currently unused */ 0,
David@0
   397
	                                  /* interface index, 0 for all available */ 0,
David@0
   398
	                                  /* registration type */ "_presence._tcp",
David@0
   399
	                                  /* domain, may be null for default */ NULL,
David@0
   400
	                                  /* callBack function */ handle_av_browse_reply,
David@0
   401
	                                  /* context, may be null */ self);
David@0
   402
David@0
   403
	if (avBrowseError == kDNSServiceErr_NoError) {
David@0
   404
		fServiceBrowser = [[ServiceController alloc] initWithServiceRef:browsRef forContactManager:self];
David@0
   405
		[fServiceBrowser addToCurrentRunLoop];
David@0
   406
	} else {
David@0
   407
		[[client client] reportError:@"Could not browse for _presence._tcp instances" ofLevel:AWEzvConnectionError];
David@0
   408
		[self disconnect];
David@0
   409
	}
David@0
   410
}
David@0
   411
David@0
   412
/* stop looking for new rendezvous clients */
David@0
   413
- (void)stopBrowsing{
David@0
   414
	AILogWithSignature(@"fServiceBrowser is %@ (retain count %i)", fServiceBrowser, [fServiceBrowser retainCount]);
David@0
   415
	[fServiceBrowser release]; fServiceBrowser = nil;
David@0
   416
}
David@0
   417
David@0
   418
/* handle a message from our browser */
David@0
   419
- (void)browseResultwithFlags:(DNSServiceFlags)flags
David@0
   420
	onInterface:(uint32_t) interfaceIndex
David@0
   421
	name:(const char *)replyName
David@0
   422
	type:(const char *)replyType
David@0
   423
	domain:(const char *)replyDomain
David@0
   424
	av:(BOOL) av {
David@0
   425
	
David@0
   426
	AWEzvContact *contact;
David@0
   427
	if (!replyName)
David@0
   428
		return;
David@0
   429
David@0
   430
	NSString *replyNameString = [NSString stringWithUTF8String:replyName];
David@0
   431
	if (!replyNameString)
David@0
   432
		return;
David@0
   433
	if (flags == (kDNSServiceFlagsAdd) || flags == (kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd)) {
David@0
   434
		/* Add this contact */
David@0
   435
		/* initialise contact */
David@0
   436
		contact = [[AWEzvContact alloc] init];
David@749
   437
		contact.uniqueID = replyNameString;
David@749
   438
		contact.manager = self;
David@0
   439
		/* save contact in dictionary */
David@0
   440
		[contacts setObject:contact forKey:replyNameString];
David@0
   441
		[contact autorelease];
David@0
   442
David@0
   443
		/* and resolve contact */
David@0
   444
		DNSServiceRef resolveRef;
David@0
   445
		DNSServiceErrorType resolveRefError;
David@0
   446
David@0
   447
		resolveRefError = DNSServiceResolve (
David@0
   448
			/* serviceref uninitialized */ &resolveRef,
David@0
   449
			/* flags, currently ignored */ 0,
David@0
   450
			/* interfaceIndex */ 0/*interfaceIndex*/,
David@0
   451
			/* full name */ replyName,
David@0
   452
			/* registration type */ "_presence._tcp" /* replyType */,
David@0
   453
			/* domain */ replyDomain,
David@0
   454
			/* callback */ resolve_reply,
David@0
   455
			/* contxt, may be NULL */ contact);
David@0
   456
David@0
   457
		if (resolveRefError == kDNSServiceErr_NoError) {
David@0
   458
			ServiceController *serviceResolver = [[ServiceController alloc] initWithServiceRef:resolveRef forContactManager:self];
David@0
   459
			[contact setResolveServiceController:serviceResolver];
David@0
   460
			[[contact resolveServiceController] addToCurrentRunLoop];
David@0
   461
			[serviceResolver release];
David@0
   462
David@0
   463
		} else {
David@0
   464
			[[client client] reportError:@"Could not search for TXT records" ofLevel:AWEzvConnectionError];
David@0
   465
			[self disconnect];
David@0
   466
		}
David@0
   467
	} else {
David@0
   468
		/* delete the contact */
David@0
   469
		contact = [contacts objectForKey:replyNameString];
David@0
   470
		if (!contact)
David@0
   471
			return;
David@0
   472
		[[client client] userLoggedOut:contact];
David@0
   473
		/* remove the contact from our data structures */
David@0
   474
		[contacts removeObjectForKey:replyNameString];
David@0
   475
		return;
David@0
   476
	}
David@0
   477
}
David@0
   478
- (void)findAddressForContact:(AWEzvContact *)contact
David@0
   479
	withHost:(NSString *)host
David@0
   480
	withInterface:(uint32_t)interface{
David@0
   481
David@0
   482
	/* Now we need to query the record for the ip address */
David@0
   483
David@0
   484
	DNSServiceErrorType err;
David@0
   485
	DNSServiceRef		serviceRef;
David@0
   486
David@0
   487
	err = DNSServiceQueryRecord( &serviceRef, (DNSServiceFlags) 0, interface, [host UTF8String], 
David@0
   488
	                             kDNSServiceType_A, kDNSServiceClass_IN, AddressQueryRecordReply, contact);
David@0
   489
	if ( err == kDNSServiceErr_NoError) {
David@0
   490
		ServiceController *temp = [[ServiceController alloc] initWithServiceRef:serviceRef forContactManager:self];
David@0
   491
		[contact setAddressServiceController:temp];
David@0
   492
		[[contact addressServiceController] addToCurrentRunLoop];
David@0
   493
		[temp release];
David@0
   494
	} else {
David@0
   495
		[[client client] reportError:@"Error finding adress for contact" ofLevel:AWEzvError];
David@0
   496
	}
David@0
   497
}
David@0
   498
David@0
   499
- (void)updateAddressForContact:(AWEzvContact *)contact
David@0
   500
	addr:(const void *)buff 
David@0
   501
	addrLen:(uint16_t)addrLen 
David@0
   502
	host:(const char*) host 
David@0
   503
	interfaceIndex:(uint32_t)interface 
David@0
   504
	more:(boolean_t)moreToCome{
David@0
   505
David@0
   506
	/* check that contact exists in dictionary */
David@749
   507
	if ([contacts objectForKey:contact.uniqueID] == nil) {
David@749
   508
		NSString *uniqueID = contact.uniqueID;
David@0
   509
		/* So they haven't been seen before... not to worry we'll add them */
David@749
   510
		if (contact.uniqueID != nil) {
David@0
   511
			contact = [[AWEzvContact alloc] init];
David@749
   512
			contact.uniqueID = uniqueID;
David@749
   513
			contact.manager = self;
David@0
   514
			/* save contact in dictionary */
David@749
   515
			[contacts setObject:contact forKey:contact.uniqueID];
David@0
   516
			[contact autorelease];
David@0
   517
David@0
   518
		} else {
David@0
   519
			[[client client] reportError:@"Contact to update not in dictionary and has bad identifier" ofLevel:AWEzvError];
David@0
   520
		}
David@0
   521
	}
David@0
   522
David@0
   523
	NSString *ipAddr;			/* ip address of contact */
David@0
   524
	NSRange	range;				/* just a range... */
David@0
   525
    
David@0
   526
	char addrBuff[256];
David@0
   527
	
David@0
   528
	inet_ntop( AF_INET, buff, addrBuff, sizeof addrBuff);
David@0
   529
David@0
   530
	ipAddr = [NSString stringWithCString:addrBuff encoding:NSUTF8StringEncoding];
David@749
   531
	range = [ipAddr rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@":"]];
David@749
   532
	if (range.location == NSNotFound) {
David@749
   533
		contact.ipAddr = ipAddr;
David@0
   534
	}
David@0
   535
David@749
   536
	if (!contact.ipAddr || !contact.ipAddr.length) {
David@0
   537
		[[client client] reportError:@"ip address not set" ofLevel:AWEzvError];
David@0
   538
		[contact setStatus: AWEzvUndefined];
David@0
   539
	}
David@0
   540
David@0
   541
	if (!moreToCome) {
David@0
   542
		[contact setAddressServiceController: nil];
David@0
   543
	}
David@0
   544
}
David@0
   545
David@0
   546
- (void)updateImageForContact:(AWEzvContact *)contact
David@0
   547
	data:(const void *)data 
David@0
   548
	dataLen:(uint16_t)dataLen
David@0
   549
	more:(boolean_t)moreToCome{
David@0
   550
David@0
   551
	if (!moreToCome) {
David@0
   552
		[contact setImageServiceController: nil];
David@0
   553
	}
Evan@663
   554
	AILogWithSignature(@"%@ -> %@ (%i)", [NSData dataWithBytes:data length:dataLen], [[[NSImage alloc] initWithData:[NSData dataWithBytes:data length:dataLen]] autorelease], dataLen);
David@0
   555
	if (dataLen != 0 ) {
David@0
   556
		/* We have an image */
David@0
   557
		/* parse raw Data */
David@0
   558
		[contact setContactImageData:[NSData dataWithBytes:data length:dataLen]];
David@0
   559
	    [[client client] userChangedImage:contact];
David@0
   560
David@0
   561
	} else {
David@0
   562
		[contact setImageHash: NULL];
David@0
   563
		[[client client] reportError:@"Error retrieving picture" ofLevel:AWEzvError];
David@0
   564
	}
David@0
   565
}
David@0
   566
- (void)updateContact:(AWEzvContact *)contact
David@0
   567
	withData:(AWEzvRendezvousData *)rendezvousData
David@0
   568
	withHost:(NSString *)host
David@0
   569
	withInterface:(uint32_t)interface
David@0
   570
	withPort:(uint16_t)recPort
David@0
   571
	av:(BOOL)av {
David@0
   572
David@0
   573
	NSString		*nick = nil;			/* nickname for contact */
David@0
   574
	NSMutableString	*mutableNick = nil;		/* nickname we can modify */
David@0
   575
	AWEzvRendezvousData	*oldrendezvous;			/* old rendezvous data for user */ /* XXX not used */
David@0
   576
	NSNumber           *idleTime = nil;			/* idle time */
David@0
   577
David@0
   578
	/* check that contact exists in dictionary */
David@749
   579
	if ([contacts objectForKey:contact.uniqueID] == nil) {
David@749
   580
		NSString *uniqueID = contact.uniqueID;
David@0
   581
		/* So they haven't been seen before... not to worry we'll add them */
David@749
   582
		if (contact.uniqueID != nil) {
David@0
   583
			contact = [[AWEzvContact alloc] init];
David@749
   584
			contact.uniqueID = uniqueID;
David@749
   585
			contact.manager = self;
David@0
   586
			/* save contact in dictionary */
David@749
   587
			[contacts setObject:contact forKey:contact.uniqueID];
David@0
   588
			[contact autorelease];
David@0
   589
		} else {
David@0
   590
			[[client client] reportError:@"Contact to update not in dictionary and has bad identifier" ofLevel:AWEzvError];
David@0
   591
		}
David@0
   592
	}
David@0
   593
David@0
   594
	if ([rendezvousData getField:@"slumming"] != nil)
David@0
   595
		// We don't want to live in a slum
David@0
   596
		return;
David@0
   597
David@0
   598
	if ([contact rendezvous] != nil) {
David@0
   599
		oldrendezvous = [contact rendezvous];
David@0
   600
		/* check serials */
David@0
   601
		if ([contact serial] > [contact serial]) {
David@749
   602
			/* AWEzvLog(@"Rendezvous update for %@ with lower serial, updating anyway", contact.uniqueID); */
David@0
   603
			/* we'll update anyway, and hopefully we'll be back in sync with the network */
David@0
   604
		}
David@0
   605
	}
David@0
   606
	[contact setRendezvous:rendezvousData];
David@0
   607
	
David@0
   608
	/* now we can update the contact */
David@0
   609
	/* get the nickname */
David@0
   610
	if ([rendezvousData getField:@"1st"] != nil)
David@0
   611
	nick = [rendezvousData getField:@"1st"];
David@0
   612
	if ([rendezvousData getField:@"last"] != nil)
David@0
   613
	if (nick == nil) {
David@0
   614
		nick = [rendezvousData getField:@"last"];
David@0
   615
	} else {
David@0
   616
		mutableNick = [[nick mutableCopy] autorelease];
David@0
   617
		[mutableNick appendString:@" "];
David@0
   618
		[mutableNick appendString:[rendezvousData getField:@"last"]];
David@0
   619
		nick = [[mutableNick copy] autorelease];
David@0
   620
	} else if (nick == nil)
David@0
   621
	    nick = @"Unnamed contact";
David@0
   622
David@0
   623
	[contact setName:nick];
David@0
   624
David@0
   625
	/* now get the status */
David@0
   626
	if ([rendezvousData getField:@"status"] == nil) {
David@0
   627
		[contact setStatus: AWEzvOnline];
David@0
   628
	} else {
David@0
   629
		if ([[rendezvousData getField:@"status"] isEqualToString:@"avail"])
David@0
   630
			[contact setStatus: AWEzvOnline];
David@0
   631
		else if ([[rendezvousData getField:@"status"] isEqualToString:@"dnd"])
David@0
   632
			[contact setStatus: AWEzvAway];
David@0
   633
		else if ([[rendezvousData getField:@"status"] isEqualToString:@"away"])
David@0
   634
			[contact setStatus: AWEzvIdle];
David@0
   635
		else
David@0
   636
			[contact setStatus: AWEzvOnline];
David@0
   637
	}
David@0
   638
	
David@0
   639
	/* Set idle time */
David@0
   640
	if ([rendezvousData getField:@"away"])
David@0
   641
		idleTime = [NSNumber numberWithLong:strtol([[rendezvousData getField:@"away"] UTF8String], NULL, 0)];
David@0
   642
	if (idleTime)
David@0
   643
		[contact setIdleSinceDate: [NSDate dateWithTimeIntervalSinceReferenceDate:[idleTime doubleValue]]];
David@0
   644
	
David@0
   645
	/* Update Buddy Icon */
David@0
   646
	if ([rendezvousData getField:@"phsh"] != nil) {
David@0
   647
		/* We should check to see if this is a new phsh */
David@0
   648
		NSString *hash = [contact imageHash];
David@0
   649
		NSString *newHash = [rendezvousData getField:@"phsh"];
Evan@663
   650
		AILogWithSignature(@"received image hash %@ for %@", newHash, contact);
David@0
   651
		if (hash == NULL || [newHash compare: hash] != NSOrderedSame) {
David@0
   652
			[contact setImageHash: newHash];	
David@0
   653
			/* The two hashes are different or there was no image before so there is an image to be downloaded */
David@0
   654
			/* Download the image using DNSServiceQueryRecord */
David@0
   655
			DNSServiceErrorType err;
David@0
   656
			DNSServiceRef		serviceRef;
David@0
   657
David@749
   658
			NSString *dnsname = [NSString stringWithFormat:@"%@%s", contact.uniqueID,"._presence._tcp.local."];
David@0
   659
			err = DNSServiceQueryRecord( &serviceRef, (DNSServiceFlags) 0, interface, [dnsname UTF8String], 
David@0
   660
			                            kDNSServiceType_NULL, kDNSServiceClass_IN, ImageQueryRecordReply, contact);
David@0
   661
			if ( err == kDNSServiceErr_NoError) {
David@0
   662
				ServiceController *temp = [[ServiceController alloc] initWithServiceRef:serviceRef forContactManager:self];
Evan@663
   663
				AILogWithSignature(@"requesting image with %@", temp);
David@0
   664
				[contact setImageServiceController:temp];
David@0
   665
				[[contact imageServiceController] addToCurrentRunLoop];
David@0
   666
				[temp release];
David@0
   667
			} else {
David@0
   668
				[contact setImageHash: NULL];
David@0
   669
				[[client client] reportError:@"Error finding image for contact" ofLevel:AWEzvError];
David@0
   670
			}
David@0
   671
		}
David@0
   672
	} else {
David@0
   673
		[contact setContactImageData:nil];
David@0
   674
		[[client client] userChangedImage:contact];
David@0
   675
	}
David@0
   676
David@0
   677
	/* now set the port */
David@561
   678
	if (recPort == 0) {
David@0
   679
		/* Couldn't find port from browse result so use port specified by txt records */
David@0
   680
		if ([rendezvousData getField:@"port.p2pj"] == nil) {
David@0
   681
			[[client client] reportError:@"Invalid rendezvous announcement for contact: no port specified" ofLevel:AWEzvError];
David@0
   682
			return;
David@0
   683
		}
David@0
   684
		[contact setPort:[[rendezvousData getField:@"port.p2pj"] intValue]];
David@0
   685
	} else {
David@0
   686
		/* Correctly use port specified by SRV record */
David@0
   687
		[contact setPort:recPort];
David@0
   688
	}
David@0
   689
	/* and notify of new user */
David@0
   690
	[[client client] userChangedState:contact];
David@0
   691
}
David@0
   692
David@0
   693
- (NSString *)myInstanceName{
David@0
   694
	return avInstanceName;
David@0
   695
}
David@0
   696
David@0
   697
- (void)setInstanceName:(NSString *)newName{
David@0
   698
	if (avInstanceName != newName) {
David@0
   699
		[avInstanceName release];
David@0
   700
		avInstanceName = [newName retain];
David@0
   701
	}
David@0
   702
}
David@0
   703
David@0
   704
- (void) regCallBack:(int)errorCode {
David@0
   705
	/* Recover if there was an error */
David@0
   706
    if (errorCode != kDNSServiceErr_NoError) {
David@0
   707
		switch (errorCode) {
David@0
   708
#warning Localize and report through the connection error system
David@0
   709
			case kDNSServiceErr_Unknown:
David@0
   710
				[[[self client] client] reportError:@"Unknown error in Bonjour Registration"
David@0
   711
						        ofLevel:AWEzvConnectionError];
David@0
   712
				break;
David@0
   713
			case kDNSServiceErr_NameConflict:
David@0
   714
				[[[self client] client] reportError:@"A user with your Bonjour data is already online"
David@0
   715
						        ofLevel:AWEzvConnectionError];
David@0
   716
				break;
David@0
   717
			default:
David@0
   718
				[[[self client] client] reportError:@"An internal error occurred"
David@0
   719
						        ofLevel:AWEzvConnectionError];
David@0
   720
				AWEzvLog(@"Internal error: rendezvous code %d", errorCode);
David@0
   721
				break;
David@0
   722
		}
David@0
   723
		/* kill connections */
David@0
   724
		[self disconnect];
David@0
   725
	} else {
David@0
   726
		[self setConnected:YES];
David@0
   727
		[self startBrowsing];
David@0
   728
	}
David@0
   729
David@0
   730
}
David@0
   731
David@0
   732
- (void)contactWillDeallocate:(AWEzvContact *)contact
David@0
   733
{
David@0
   734
	[[client client] userLoggedOut:contact];
David@0
   735
}
David@0
   736
David@0
   737
- (void)serviceControllerReceivedFatalError:(ServiceController *)serviceController
David@0
   738
{
David@0
   739
	[[[self client] client] reportError:@"An unrecoverable connection error occurred"
David@0
   740
						        ofLevel:AWEzvConnectionError];
David@0
   741
	[self disconnect];
David@0
   742
}
David@0
   743
David@0
   744
@end
David@0
   745
David@0
   746
#pragma mark mDNS callbacks
David@0
   747
David@0
   748
#pragma mark mDNS Register Callbacks
David@0
   749
void register_reply (DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context ) {
David@0
   750
	AWEzvContactManager *self = context;
David@0
   751
	[self setInstanceName:[NSString stringWithUTF8String:name]];
David@0
   752
	[self regCallBack:errorCode];
David@0
   753
}
David@0
   754
David@0
   755
void image_register_reply ( 
David@0
   756
	DNSServiceRef sdRef, 
David@0
   757
	DNSRecordRef RecordRef, 
David@0
   758
	DNSServiceFlags flags, 
David@0
   759
	DNSServiceErrorType errorCode, 
David@0
   760
	void *context ) {
David@0
   761
	if (errorCode != kDNSServiceErr_NoError) {
David@0
   762
		AWEzvLog(@"error %d registering image record", errorCode);
David@0
   763
	} else {
David@0
   764
		AWEzvContactManager *self = context;
David@0
   765
		[self updatePHSH];
David@0
   766
	}
David@0
   767
}
David@0
   768
#pragma mark mDNS Browse Callback
David@0
   769
David@0
   770
/*!
David@0
   771
 * @brief DNSServiceBrowse callback
David@0
   772
 *
David@0
   773
 * This may be called multiple times for a single use of DNSServiceBrowse().
David@0
   774
 */
David@0
   775
void handle_av_browse_reply (DNSServiceRef sdRef,
David@0
   776
		DNSServiceFlags flags,
David@0
   777
		uint32_t interfaceIndex,
David@0
   778
		DNSServiceErrorType errorCode,
David@0
   779
		const char *serviceName,
David@0
   780
		const char *regtype,
David@0
   781
		const char *replyDomain,
David@0
   782
		void *context ) {
David@0
   783
	/* Received a browser reply from DNSServiceBrowse for av, now must handle processing the list of results */
David@0
   784
	if (errorCode == kDNSServiceErr_NoError) {
David@0
   785
		AWEzvContactManager *self = context;
David@0
   786
	    if (![[self myInstanceName] isEqualToString:[NSString stringWithUTF8String:serviceName]])
David@0
   787
			[self browseResultwithFlags:flags onInterface:interfaceIndex name:serviceName type:regtype domain:replyDomain av:YES];
David@0
   788
	} else {
David@0
   789
		AWEzvLog(@"Error browsing");
David@0
   790
	}
David@0
   791
}
David@0
   792
David@0
   793
#pragma mark mDNS Resolve Callback
David@0
   794
/*!
David@0
   795
* @brief DNSServiceResolve callback
David@0
   796
 *
David@0
   797
 * This may be called multiple times for a single use of DNSServiceResolve().
David@0
   798
 */
David@0
   799
void resolve_reply (DNSServiceRef sdRef, 
David@0
   800
					DNSServiceFlags flags, 
David@0
   801
					uint32_t interfaceIndex, 
David@0
   802
					DNSServiceErrorType errorCode, 
David@0
   803
					const char *fullname, 
David@0
   804
					const char *hosttarget, 
David@0
   805
					uint16_t port, 
David@0
   806
					uint16_t txtLen, 
David@571
   807
					const unsigned char *txtRecord, 
David@0
   808
					void *context)
David@0
   809
{
David@0
   810
	if (errorCode == kDNSServiceErr_NoError) {
David@0
   811
		/* use TXTRecord methods to resolve this */
David@0
   812
		AWEzvContact	*contact = context;
David@0
   813
		AWEzvContactManager *self = [contact manager];
David@0
   814
		//AWEzvLog(@"Would update contact");
David@0
   815
		AWEzvRendezvousData *data;
David@0
   816
		data = [[[AWEzvRendezvousData alloc] initWithTXTRecordRef:txtRecord length:txtLen] autorelease];
David@0
   817
		[self findAddressForContact:contact withHost:[NSString stringWithUTF8String:hosttarget] withInterface:interfaceIndex];
David@0
   818
		[self updateContact:contact withData:data withHost:[NSString stringWithUTF8String:hosttarget] withInterface:interfaceIndex withPort:ntohs(port) av:YES];
David@0
   819
David@0
   820
	} else {
David@0
   821
		AWEzvLog(@"Error resolving records");
David@0
   822
	}	
David@0
   823
}
David@0
   824
David@0
   825
#pragma mark mDNS Address Callback
David@0
   826
David@0
   827
void AddressQueryRecordReply( DNSServiceRef DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, 
David@0
   828
								DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, 
David@0
   829
								uint16_t rdlen, const void *rdata, uint32_t ttl, void *context )
David@0
   830
// DNSServiceQueryRecord callback used to look up IP addresses.
David@0
   831
{
David@0
   832
	AWEzvContact	*contact = context;
David@0
   833
	AWEzvContactManager *self = [contact manager];
David@0
   834
David@0
   835
	[self updateAddressForContact:contact addr:rdata addrLen:rdlen host:fullname interfaceIndex:interfaceIndex 
David@0
   836
						more:((flags & kDNSServiceFlagsMoreComing) != 0)];
David@0
   837
David@0
   838
}
David@0
   839
David@0
   840
#pragma mark mDNS Image Callback
David@0
   841
void ImageQueryRecordReply( DNSServiceRef DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, 
David@0
   842
								DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, 
David@0
   843
								uint16_t rdlen, const void *rdata, uint32_t ttl, void *context )
David@0
   844
// DNSServiceQueryRecord callback used to look up buddy icon.
David@0
   845
{
David@0
   846
	AWEzvContact	*contact = context;
David@0
   847
	AWEzvContactManager *self = [contact manager];
David@0
   848
	if (errorCode == kDNSServiceErr_NoError) {
David@0
   849
		if (flags & kDNSServiceFlagsAdd)
David@0
   850
			[self updateImageForContact:contact data:rdata dataLen:rdlen more:((flags & kDNSServiceFlagsMoreComing) != 0)];
David@0
   851
	}
David@0
   852
}
David@0
   853
David@0
   854
#pragma mark Service Controller
David@0
   855
David@0
   856
/* ServiceController was taken from Apple's DNSServiceBrowser.m */
David@0
   857
@implementation ServiceController : NSObject
David@0
   858
David@0
   859
#pragma mark CFSocket Callback
David@0
   860
/* This code was taken from Apple's DNSServiceBrowser.m */
David@0
   861
static void	ProcessSockData( CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
David@0
   862
// CFRunloop callback that notifies dns_sd when new data appears on a DNSServiceRef's socket.
David@0
   863
{
David@0
   864
	ServiceController *self = (ServiceController *)info;
Evan@663
   865
	AILogWithSignature(@"Processing result for %@", self);
David@0
   866
David@0
   867
	DNSServiceErrorType err = DNSServiceProcessResult([self serviceRef]);
David@0
   868
	if (err != kDNSServiceErr_NoError) {
David@0
   869
		if ((err == kDNSServiceErr_Unknown) && !data) {
David@0
   870
			//Try to accept(2) a connection. May be the cause of a hang on Tiger; see #7887.
David@0
   871
			int socketFD = CFSocketGetNative(s);
David@565
   872
			int childFD = accept(socketFD, /*addr*/ NULL, /*addrlen*/ NULL);
David@0
   873
			AILog(@"%@: Service ref %p received an unknown error with no data; perhaps mDNSResponder crashed? Result of calling accept(2) on fd %d is %d; will disconnect with error",
David@0
   874
				  self, [self serviceRef], socketFD, childFD);
David@0
   875
			//We don't actually *want* a connection, so close the socket immediately.
David@0
   876
			if (childFD > -1) close(childFD);
David@0
   877
David@0
   878
			[self retain];
David@0
   879
			[[self contactManager] serviceControllerReceivedFatalError:self];
David@0
   880
			[self breakdownServiceController];
David@0
   881
			[self release];
David@0
   882
David@0
   883
		} else {
David@0
   884
			AILog(@"DNSServiceProcessResult() for socket descriptor %d returned an error! %d with CFSocketCallBackType %d and data %s\n",
David@0
   885
				  DNSServiceRefSockFD(info), err, type, data);
David@0
   886
		}
David@0
   887
	}
David@0
   888
}
David@0
   889
David@0
   890
- (id) initWithServiceRef:(DNSServiceRef) ref forContactManager:(AWEzvContactManager *)inContactManager
David@0
   891
{
David@0
   892
	[super init];
David@0
   893
	fServiceRef = ref;
David@0
   894
	contactManager = [inContactManager retain];
David@0
   895
David@0
   896
	return self;
David@0
   897
}
David@0
   898
David@0
   899
- (boolean_t) addToCurrentRunLoop
David@0
   900
/* Add the service to the current runloop. Returns non-zero on success. */
David@0
   901
{
David@0
   902
	CFSocketContext			ctx = { 1, self, NULL, NULL, NULL };
David@0
   903
David@0
   904
	fSocketRef = CFSocketCreateWithNative(kCFAllocatorDefault, DNSServiceRefSockFD(fServiceRef),
David@0
   905
										kCFSocketReadCallBack, ProcessSockData, &ctx);
David@0
   906
	if (fSocketRef != NULL)
David@0
   907
		fRunloopSrc = CFSocketCreateRunLoopSource(kCFAllocatorDefault, fSocketRef, 1);
David@0
   908
	if (fRunloopSrc != NULL) {
David@0
   909
		AILogWithSignature(@"Adding run loop source %p from run loop %p", fRunloopSrc, CFRunLoopGetCurrent());
David@0
   910
		CFRunLoopAddSource(CFRunLoopGetCurrent(), fRunloopSrc, kCFRunLoopDefaultMode);
David@0
   911
	} else
David@0
   912
		AILog(@"%@: Could not listen to runloop socket", self);
David@0
   913
David@0
   914
	return (fRunloopSrc != NULL);
David@0
   915
}
David@0
   916
David@0
   917
- (DNSServiceRef) serviceRef
David@0
   918
{
David@0
   919
	return fServiceRef;
David@0
   920
}
David@0
   921
David@0
   922
- (AWEzvContactManager *)contactManager
David@0
   923
{
David@0
   924
	return contactManager;
David@0
   925
}
David@0
   926
David@0
   927
- (void) dealloc
David@0
   928
/* Remove service from runloop, deallocate service and associated resources */
David@0
   929
{
David@0
   930
	AILogWithSignature(@"%@", self);
David@0
   931
David@0
   932
	[self breakdownServiceController];
David@0
   933
David@0
   934
	[super dealloc];
David@0
   935
}
David@0
   936
David@0
   937
- (void)breakdownServiceController
David@0
   938
{
David@0
   939
	AILogWithSignature(@"%@", self);
David@0
   940
David@0
   941
	if (fSocketRef != NULL) {
David@0
   942
		CFSocketInvalidate(fSocketRef);		// Note: Also closes the underlying socket
David@0
   943
		CFRelease(fSocketRef);
David@0
   944
		fSocketRef = NULL;
David@0
   945
	}
David@0
   946
David@0
   947
	if (fRunloopSrc != NULL) {
David@0
   948
		AILogWithSignature(@"Removing run loop source %p from run loop %p", fRunloopSrc, CFRunLoopGetCurrent());
David@0
   949
		CFRunLoopRemoveSource(CFRunLoopGetCurrent(), fRunloopSrc, kCFRunLoopDefaultMode);
David@0
   950
		CFRelease(fRunloopSrc);
David@0
   951
		fRunloopSrc = NULL;
David@0
   952
	}
David@0
   953
David@0
   954
	if (fServiceRef) {
David@0
   955
		AILogWithSignature(@"Deallocating DNSServiceRef %p", fServiceRef);
David@0
   956
David@0
   957
		DNSServiceRefDeallocate(fServiceRef);
David@0
   958
		fServiceRef = NULL;
David@0
   959
	}
David@0
   960
David@0
   961
	[contactManager release]; contactManager = nil;
David@0
   962
}
David@0
   963
David@0
   964
@end // implementation ServiceController