660 lines
22 KiB
Mathematica
660 lines
22 KiB
Mathematica
|
//
|
||
|
// FireflyServer.m
|
||
|
//
|
||
|
// The "model" part of our Model-View-Controller trio. Represents the
|
||
|
// firefly server itself, and encapsulates launching, quitting, status,
|
||
|
// etc.
|
||
|
//
|
||
|
// Created by Mike Kobb on 7/12/06.
|
||
|
// Copyright 2006 Roku, LLC. All rights reserved.
|
||
|
//
|
||
|
#import <CoreFoundation/CoreFoundation.h>
|
||
|
#import <netinet/in.h> // for sockaddr_in
|
||
|
#include <arpa/inet.h> // for inet_ntoa and so forth
|
||
|
#include <sys/socket.h> // AF_INET6
|
||
|
#import "FireflyServer.h"
|
||
|
|
||
|
@implementation FireflyServer
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// initWithServerPath
|
||
|
//
|
||
|
// Initialize the server object to manage the server at the given path.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (id)initWithServerPath:(NSString *) path
|
||
|
{
|
||
|
if( ( self = [super init] ) != nil )
|
||
|
{
|
||
|
fireflyServerPath = [[NSString stringWithString:path] retain];
|
||
|
serverTask = nil;
|
||
|
delegate = nil;
|
||
|
serverVersion = nil;
|
||
|
serverURL = nil;
|
||
|
status = kFireflyStatusStopped;
|
||
|
|
||
|
// Bonjour stuff below
|
||
|
netBrowser = [[NSNetServiceBrowser alloc] init];
|
||
|
pendingNetServices = [[NSMutableArray arrayWithCapacity:5] retain];
|
||
|
fireflyService = nil;
|
||
|
|
||
|
// Pick a random ffid that we'll be able to use to identify our
|
||
|
// server easily
|
||
|
srand((unsigned int)time(NULL));
|
||
|
sprintf( ffid, "%08x", rand() );
|
||
|
|
||
|
// Register for task ending notifications, so we can find out
|
||
|
// if our server process quits.
|
||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||
|
selector:@selector(taskEnded:)
|
||
|
name:NSTaskDidTerminateNotification
|
||
|
object:nil];
|
||
|
}
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// dealloc
|
||
|
//
|
||
|
//
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)dealloc
|
||
|
{
|
||
|
// First, kill the server!
|
||
|
|
||
|
|
||
|
[fireflyServerPath release];
|
||
|
[serverTask release];
|
||
|
[serverVersion release];
|
||
|
[serverURL release];
|
||
|
[netBrowser release];
|
||
|
[pendingNetServices release];
|
||
|
[fireflyService release];
|
||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||
|
[super dealloc];
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// setup
|
||
|
//
|
||
|
// Not to be confused with 'start', this function is to be called before
|
||
|
// starting to use the object. It handles starting our Bonjour stuff and
|
||
|
// so on.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)setup
|
||
|
{
|
||
|
// It's time to start our scan, limiting to the local host.
|
||
|
[netBrowser setDelegate:self];
|
||
|
[netBrowser searchForServicesOfType:@"_http._tcp." inDomain:@"local."];
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// shutdown
|
||
|
//
|
||
|
// Not to be confused with 'stop', this function is to be called when
|
||
|
// preparing to dispose the object. It handles shutting down our Bonjour
|
||
|
// stuff and so on. But, it does also stop the server if it's running.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)shutdown
|
||
|
{
|
||
|
// shut down the firefly server
|
||
|
[self setStatus:kFireflyStatusStopping];
|
||
|
[self stop];
|
||
|
|
||
|
// Now shut down the Bonjour scan.
|
||
|
[netBrowser stop];
|
||
|
|
||
|
// FIXME: Is it safe to do this right after calling stop, or should we
|
||
|
// put these in didStop?
|
||
|
NSEnumerator *enumerator = [pendingNetServices objectEnumerator];
|
||
|
NSNetService *service;
|
||
|
while( service = [enumerator nextObject] )
|
||
|
[service stop];
|
||
|
[pendingNetServices removeAllObjects];
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// setDelegate
|
||
|
//
|
||
|
// We will message our delegate when important things happen, like the server
|
||
|
// starting or stopping, etc.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)setDelegate:(id) delegateToSet
|
||
|
{
|
||
|
delegate = delegateToSet;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// isRunning
|
||
|
//
|
||
|
// Is the server running?
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (BOOL)isRunning
|
||
|
{
|
||
|
BOOL retVal = NO;
|
||
|
if( nil != serverTask )
|
||
|
retVal = [serverTask isRunning];
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// start
|
||
|
//
|
||
|
// Starts the server. Note that this function may fail if the server is
|
||
|
// already running.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (BOOL)start
|
||
|
{
|
||
|
BOOL retVal = NO;
|
||
|
|
||
|
FireflyServerStatus curStatus = [self status];
|
||
|
if( curStatus == kFireflyStatusStopped ||
|
||
|
curStatus == kFireflyStatusStartFailed ||
|
||
|
curStatus == kFireflyStatusCrashed )
|
||
|
{
|
||
|
retVal = [self startAndUpdateStatus:YES];
|
||
|
}
|
||
|
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// stop
|
||
|
//
|
||
|
// Signals the server to stop. Returns YES if the signal was sent successfully
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (BOOL)stop
|
||
|
{
|
||
|
BOOL retVal = NO;
|
||
|
if( nil != serverTask )
|
||
|
{
|
||
|
[self setStatus:kFireflyStatusStopping];
|
||
|
[serverTask terminate];
|
||
|
retVal = YES;
|
||
|
}
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// restart
|
||
|
//
|
||
|
// restarts the server. Tells the server to shut down after setting our
|
||
|
// status to "restarting". When the server shuts down, the taskEnded
|
||
|
// method will see that status, and restart the server.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (BOOL)restart
|
||
|
{
|
||
|
BOOL retVal = NO;
|
||
|
if( nil != serverTask )
|
||
|
{
|
||
|
[self setStatus:kFireflyStatusRestarting];
|
||
|
[serverTask terminate];
|
||
|
retVal = YES;
|
||
|
}
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// status
|
||
|
//
|
||
|
// Returns the current server status
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (FireflyServerStatus)status
|
||
|
{
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// version
|
||
|
//
|
||
|
// Returns the current server version, or nil if it's not yet known
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (NSString *)version
|
||
|
{
|
||
|
return serverVersion;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// configURL
|
||
|
//
|
||
|
// Returns the server's advanced user configuration URL, or nil if it's not
|
||
|
// yet known
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (NSString *)configURL
|
||
|
{
|
||
|
return serverURL;
|
||
|
}
|
||
|
|
||
|
// ===========================================================================
|
||
|
// Private utilities.
|
||
|
// ===========================================================================
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// setStatus
|
||
|
//
|
||
|
// Sets the status and notifies interested parties of the change.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)setStatus:(FireflyServerStatus) newStatus
|
||
|
{
|
||
|
status = newStatus;
|
||
|
|
||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@STATUS_CHANGE
|
||
|
object:self];
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// setURL
|
||
|
//
|
||
|
// Sets the server config URL and notifies interested parties of the change.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)setURL:(NSString *) newUrl
|
||
|
{
|
||
|
[serverURL autorelease];
|
||
|
serverURL = [[NSString stringWithString:newUrl] retain];
|
||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@URL_CHANGE
|
||
|
object:self];
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// setVersion
|
||
|
//
|
||
|
// Sets the server version and notifies interested parties of the change.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)setVersion:(NSString *)newVersion
|
||
|
{
|
||
|
[serverVersion autorelease];
|
||
|
serverVersion = [[NSString stringWithString:newVersion] retain];
|
||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@VERSION_CHANGE
|
||
|
object:self];
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// taskEnded
|
||
|
//
|
||
|
// We register this function to be called when tasks end. If the task is
|
||
|
// our server task, then we dispose the (now useless) object and notify
|
||
|
// interested parties of the change. We check for normal versus abnormal
|
||
|
// termination and set status accordingly.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)taskEnded:(NSNotification *)notification
|
||
|
{
|
||
|
if( serverTask == [notification object] )
|
||
|
{
|
||
|
int termStatus = [[notification object] terminationStatus];
|
||
|
[serverTask autorelease];
|
||
|
serverTask = nil;
|
||
|
if( kFireflyStatusRestarting == status )
|
||
|
{
|
||
|
// Don't post the message saying that the server stopped;
|
||
|
// just start up and let the success or failure of that startup
|
||
|
// handle the status update.
|
||
|
[self startAndUpdateStatus:NO];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( 0 == termStatus )
|
||
|
[self setStatus:kFireflyStatusStopped];
|
||
|
else if( kFireflyStatusStarting == status )
|
||
|
[self setStatus:kFireflyStatusStartFailed];
|
||
|
else
|
||
|
[self setStatus:kFireflyStatusCrashed];
|
||
|
NSLog(@"Server Task ended with status %d\n", termStatus);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// fireflyConfigFilePath
|
||
|
//
|
||
|
// Build the path to the config file, test that it's valid and return it. If
|
||
|
// we can't find a file at the expected location, we return nil
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (NSString*)fireflyConfigFilePath
|
||
|
{
|
||
|
NSString *retVal = nil;
|
||
|
NSArray * appSupportDirArray =
|
||
|
NSSearchPathForDirectoriesInDomains( NSLibraryDirectory,
|
||
|
NSUserDomainMask,
|
||
|
YES );
|
||
|
if( [appSupportDirArray count] > 0 )
|
||
|
{
|
||
|
BOOL bIsDir = NO;
|
||
|
NSFileManager *mgr = [NSFileManager defaultManager];
|
||
|
NSString *configFilePath =
|
||
|
[[appSupportDirArray objectAtIndex:0]
|
||
|
stringByAppendingPathComponent:@"Application Support/Firefly/"
|
||
|
FIREFLY_CONF_NAME];
|
||
|
if( [mgr fileExistsAtPath:configFilePath isDirectory:&bIsDir] && !bIsDir )
|
||
|
retVal = configFilePath;
|
||
|
}
|
||
|
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// startAndUpdateStatus
|
||
|
//
|
||
|
// Private utility that actually starts the server. If the bUpdate flag is
|
||
|
// NO, then this utility will leave the current status in place, even though
|
||
|
// the server is starting. This is intended for use by the restart function,
|
||
|
// so that the status will remain in "restarting" until the server actually
|
||
|
// comes online (or fails)
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (BOOL)startAndUpdateStatus:(BOOL)bUpdate
|
||
|
{
|
||
|
BOOL retVal = NO;
|
||
|
|
||
|
[self killRunningFireflies];
|
||
|
|
||
|
NSString *configFilePath = [self fireflyConfigFilePath];
|
||
|
if( nil != configFilePath )
|
||
|
{
|
||
|
NSArray *array = [NSArray arrayWithObjects:
|
||
|
@"-y", @"-f", @"-c",
|
||
|
configFilePath,
|
||
|
@"-b",
|
||
|
[NSString stringWithUTF8String:ffid], // best 10.4<->10.3 compromise method...
|
||
|
nil];
|
||
|
@try
|
||
|
{
|
||
|
serverTask = [[[NSTask alloc] init] retain];
|
||
|
[serverTask setLaunchPath:fireflyServerPath];
|
||
|
[serverTask setCurrentDirectoryPath:[fireflyServerPath stringByDeletingLastPathComponent]];
|
||
|
[serverTask setArguments:array];
|
||
|
|
||
|
if( bUpdate )
|
||
|
[self setStatus:kFireflyStatusStarting];
|
||
|
[serverTask launch];
|
||
|
retVal = YES;
|
||
|
}
|
||
|
@catch( NSException *exception )
|
||
|
{
|
||
|
if( [[exception name] isEqual:NSInvalidArgumentException] )
|
||
|
;
|
||
|
NSLog(@"FireflyServer: Caught %@: %@", [exception name], [exception reason]);
|
||
|
[self setStatus:kFireflyStatusStartFailed];
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NSLog(@"couldn't find config file at %@\n", configFilePath);
|
||
|
}
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// killRunningFireflies
|
||
|
//
|
||
|
// This may seem like paranoia, but things really go badly if there is more
|
||
|
// than one copy of Firefly running (e.g. started from the command line, or
|
||
|
// failing to quit when signaled). So, we enforce some preconditions here.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)killRunningFireflies
|
||
|
{
|
||
|
kinfo_proc *result;
|
||
|
size_t length;
|
||
|
GetProcesses( &result, &length );
|
||
|
|
||
|
// Okay, now we have our list of processes. Let's find OUR copy of
|
||
|
// firefly. Note that Firefly runs as multiple processes, so we look
|
||
|
// through all processes, not stopping when we find one. We *are*
|
||
|
// careful to only kill processes owned by the current user!
|
||
|
if( NULL != result )
|
||
|
{
|
||
|
int procCount = length / sizeof(kinfo_proc);
|
||
|
int i = 0;
|
||
|
uid_t ourUID = getuid();
|
||
|
for( ; i < procCount; i++ )
|
||
|
{
|
||
|
if( ourUID == result[i].kp_eproc.e_pcred.p_ruid &&
|
||
|
0 == strcasecmp( result[i].kp_proc.p_comm, FIREFLY_SERVER_NAME ) )
|
||
|
{
|
||
|
NSLog(@"Killing rogue firefly, pid %d\n", result[i].kp_proc.p_pid);
|
||
|
kill( result[i].kp_proc.p_pid, SIGKILL );
|
||
|
}
|
||
|
}
|
||
|
free( result );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ===========================================================================
|
||
|
// Below are the delegate methods for Bonjour discovery of the server.
|
||
|
// ===========================================================================
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// netServiceBrowserWillSearch:
|
||
|
//
|
||
|
// Lets us know that the Bonjour search has started
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)netServiceBrowser
|
||
|
{
|
||
|
#ifdef FIREFLY_DEBUG
|
||
|
NSLog(@"NSNetServiceBrowser started\n");
|
||
|
#endif
|
||
|
bScanIsActive = YES;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// netServiceBrowserDidStopSearch:
|
||
|
//
|
||
|
// Should only stop if we ask it to, as when we're quitting the app
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)netServiceBrowser
|
||
|
{
|
||
|
#ifdef FIREFLY_DEBUG
|
||
|
NSLog(@"NSNetServiceBrowser stopped\n");
|
||
|
#endif
|
||
|
bScanIsActive = NO;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// netServiceBrowser:didRemoveService:moreComing:
|
||
|
//
|
||
|
// Called when a Bonjour service goes away.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser
|
||
|
didRemoveService:(NSNetService *)netService
|
||
|
moreComing:(BOOL)moreServicesComing
|
||
|
{
|
||
|
// Is it our service? If so, we need to switch the status text and change
|
||
|
// the start/stop button, and also update the web page button and text.
|
||
|
if( [netService isEqual:fireflyService] )
|
||
|
{
|
||
|
[fireflyService autorelease];
|
||
|
fireflyService = nil;
|
||
|
// FIXME: AND? Theoretically, we should be notified that our NSTask
|
||
|
// went away, so this notification isn't needed to detect that the
|
||
|
// server stopped. But, what if due to some error, the Bonjour
|
||
|
// service croaked but left the server itself running?
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// netServiceBrowser:didRemoveDomain:moreComing:
|
||
|
//
|
||
|
// unless our local host goes away, we really don't care.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser
|
||
|
didRemoveDomain:(NSString *)domainName
|
||
|
moreComing:(BOOL)moreDomainsComing
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// netServiceBrowser:didNotSearch:
|
||
|
//
|
||
|
// Called if the search failed to start. We need to alert the user.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser
|
||
|
didNotSearch:(NSDictionary *)errorInfo
|
||
|
{
|
||
|
// FIXME: display error info? Try again? Quit?
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// netServiceBrowser:didFindService:moreComing:
|
||
|
//
|
||
|
// A Bonjour service has been discovered. It might be our server. We need
|
||
|
// to ask the service to resolve, so we can see if it is.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser
|
||
|
didFindService:(NSNetService *)netService
|
||
|
moreComing:(BOOL)moreServicesComing
|
||
|
{
|
||
|
// We need to ask this object to resolve itself so we can figure out if it's the server
|
||
|
// we want. In case the user closes the panel, we need to have a list of all the ones
|
||
|
// pending so we can stop them.
|
||
|
[pendingNetServices addObject:netService];
|
||
|
[netService setDelegate:self];
|
||
|
[netService resolve];
|
||
|
|
||
|
if( !moreServicesComing )
|
||
|
bScanIsActive = NO;
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// netServiceBrowser:didFindDomain:moreComing:
|
||
|
//
|
||
|
// Don't think we care about this one, but I'm pretty sure we're supposed to
|
||
|
// implement it (why?)
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser
|
||
|
didFindDomain:(NSString *)domainName
|
||
|
moreComing:(BOOL)moreDomainsComing
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// netServiceDidResolveAddress:
|
||
|
//
|
||
|
// We asked a service to resolve, and it has. Time to check to see if it's
|
||
|
// our server.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)netServiceDidResolveAddress:(NSNetService *)service
|
||
|
{
|
||
|
// Is it a firefly service, and if so, is it ours?
|
||
|
|
||
|
// NOTE: EXTREMELY IMPORTANT!
|
||
|
// protocolSpecificInformation is a deprecated API, and the Tiger-on approved way is TXTRecordData.
|
||
|
// protocolSpecificInformation, though, is available back to 10.2. These return the data in
|
||
|
// DIFFERENT FORMATS. TXTRecordData returns the data in the mDNS-standard format of a packed
|
||
|
// array of Pascal strings, while protocolSpecificInformation returns the strings separated by 0x01
|
||
|
// characters. The TXTRecordContainsKey function and related utilities assume (at least on Tiger)
|
||
|
// the packed-Pascal format, so we have to use strstr instead. Happily, all we really care
|
||
|
// about here is that the ffid tag exists, so we don't need to do much parsing.
|
||
|
const char *version = NULL;
|
||
|
const char* txtRecordBytes = NULL;
|
||
|
NSData *data = nil;
|
||
|
|
||
|
NSString *txtRecord = [service protocolSpecificInformation];
|
||
|
if( nil != txtRecord )
|
||
|
data = [txtRecord dataUsingEncoding:NSUTF8StringEncoding];
|
||
|
txtRecordBytes = (const char*)[data bytes];
|
||
|
if( NULL != txtRecordBytes &&
|
||
|
NULL != ( version = strnstr( txtRecordBytes, "\001mtd-version=", [data length] ) ) )
|
||
|
{
|
||
|
// Okay, this is a firefly server. Let's see if it's *our* server
|
||
|
int i = 0;
|
||
|
char buf[256]; // max allowed size, but we'll still be careful not to overrun.
|
||
|
strncpy( buf, txtRecordBytes, 255 );
|
||
|
buf[255] = '\0';
|
||
|
#ifdef FIREFLY_DEBUG
|
||
|
NSLog( @"Text record is: %s\n", buf );
|
||
|
#endif
|
||
|
const char *ffidptr = strnstr( txtRecordBytes, "\001ffid=", [data length] );
|
||
|
if( NULL != ffidptr )
|
||
|
{
|
||
|
// This is a bit of a pain due to the stuff described in the big
|
||
|
// comment above.
|
||
|
|
||
|
// advance over the key
|
||
|
ffidptr += 6;
|
||
|
while( '\0' != ffidptr[i] &&
|
||
|
'\001' != ffidptr[i] &&
|
||
|
((ffidptr-txtRecordBytes)+i) < [data length] )
|
||
|
{
|
||
|
#ifdef FIREFLY_DEBUG
|
||
|
NSLog(@"Adding %c (%d)to ffidptr\n", ffidptr[i], ffidptr[i]);
|
||
|
#endif
|
||
|
buf[i] = ffidptr[i++];
|
||
|
}
|
||
|
buf[i] = '\0';
|
||
|
NSLog(@"Comparing buf %s against our ffid %s\n", buf, ffid);
|
||
|
if( 0 == strcmp( buf, ffid ) )
|
||
|
{
|
||
|
// WOOT! This is us. Get the version and port
|
||
|
i = 0;
|
||
|
version += 13;
|
||
|
while( '\0' != version[i] &&
|
||
|
'\001' != version[i] &&
|
||
|
((version-txtRecordBytes)+i) < [data length] )
|
||
|
buf[i] = version[i++];
|
||
|
buf[i] = '\0';
|
||
|
[self setVersion:[NSString stringWithUTF8String:buf]];
|
||
|
|
||
|
// Time to get the port.
|
||
|
NSArray *svcAddresses = [service addresses];
|
||
|
if( 0 != [svcAddresses count] )
|
||
|
{
|
||
|
NSData *addrData = [svcAddresses objectAtIndex:0];
|
||
|
struct sockaddr_in *addrPtr = (struct sockaddr_in*)[addrData bytes];
|
||
|
if( NULL != addrPtr )
|
||
|
{
|
||
|
serverPort = ntohs(addrPtr->sin_port);
|
||
|
[self setURL:[NSString stringWithFormat:@"http://localhost:%u", serverPort]];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Okay, it's the one we want, so let's remember it and update
|
||
|
// our status
|
||
|
[fireflyService autorelease];
|
||
|
fireflyService = [service retain];
|
||
|
[self setStatus:kFireflyStatusActive];
|
||
|
#ifdef FIREFLY_DEBUG
|
||
|
NSBeep();
|
||
|
sleep(1);
|
||
|
NSBeep();
|
||
|
sleep(1);
|
||
|
NSBeep();
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// It's no longer pending, so remove from array. If it was ours, we've
|
||
|
// retained it.
|
||
|
[pendingNetServices removeObject:service];
|
||
|
|
||
|
// If we're no longer scanning and we've exhausted all the
|
||
|
// services that we found without identifying the correct server,
|
||
|
// we need to ...?
|
||
|
if( !bScanIsActive && 0 == [pendingNetServices count] && nil == fireflyService )
|
||
|
; //FIXME
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// netService:didNotResolve:
|
||
|
//
|
||
|
// We tried to resolve a service, and failed. It's probably not really
|
||
|
// running. We could always try again, but it doesn't seem to be necessary
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)netService:(NSNetService *)service didNotResolve:(NSDictionary *)errorInfo
|
||
|
{
|
||
|
[pendingNetServices removeObject:service];
|
||
|
#ifdef FIREFLY_DEBUG
|
||
|
if( nil == fireflyService )
|
||
|
NSLog(@"Failed to resolve service: %@\n", [errorInfo valueForKey:NSNetServicesErrorCode] );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------
|
||
|
// netServiceWillResolve:
|
||
|
//
|
||
|
// Just lets us know resolution has started.
|
||
|
// ---------------------------------------------------------------------------
|
||
|
- (void)netServiceWillResolve:(NSNetService *)service
|
||
|
{
|
||
|
}
|
||
|
|
||
|
@end
|