2008-02-03 10:41:23 +00:00
//
// LinkBackServer . m
// LinkBack
//
// Created by Charles Jolley on Tue Jun 15 2004.
// Copyright ( c ) 2004 , Nisus Software , Inc .
// All rights reserved .
// Redistribution and use in source and binary forms , with or without
// modification , are permitted provided that the following conditions are met :
//
// Redistributions of source code must retain the above copyright notice ,
// this list of conditions and the following disclaimer .
//
// Redistributions in binary form must reproduce the above copyright notice ,
// this list of conditions and the following disclaimer in the documentation
// and / or other materials provided with the distribution .
//
// Neither the name of the Nisus Software , Inc . nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission .
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS
// IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO ,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL ,
// EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO ,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR
// PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
// NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
//
# import "LinkBackServer.h"
# import "LinkBack.h"
NSString * MakeLinkBackServerName ( NSString * bundleIdentifier , NSString * name )
{
return [ bundleIdentifier stringByAppendingFormat : @ ":%@" , name ] ;
}
NSMutableDictionary * LinkBackServers = nil ;
@ implementation LinkBackServer
+ ( void ) initialize
{
static BOOL inited = NO ;
if ( inited ) return ;
[ super initialize ] ;
inited = YES ;
if ( ! LinkBackServers ) LinkBackServers = [ [ NSMutableDictionary alloc ] init ] ;
}
+ ( LinkBackServer * ) LinkBackServerWithName : ( NSString * ) aName
{
return [ LinkBackServers objectForKey : aName ] ;
}
+ ( BOOL ) publishServerWithName : ( NSString * ) aName delegate : ( id < LinkBackServerDelegate > ) del
{
LinkBackServer * serv = [ [ LinkBackServer alloc ] initWithName : aName delegate : del ] ;
BOOL ret = [ serv publish ] ; // retains if successful
[ serv release ] ;
return ret ;
}
BOOL LinkBackServerIsSupported ( NSString * name , id supportedServers )
{
BOOL ret = NO ;
2011-02-20 11:12:35 +00:00
# if defined ( __MAC _OS _X _VERSION _MAX _ALLOWED ) && ( __MAC _OS _X _VERSION _MAX _ALLOWED >= 1050 )
2010-12-16 08:03:24 +00:00
NSUInteger idx ;
2011-02-20 11:12:35 +00:00
# else
int idx ;
# endif
2008-02-03 10:41:23 +00:00
NSString * curServer = supportedServers ;
// NOTE : supportedServers may be nil , an NSArray , or NSString .
if ( supportedServers ) {
if ( [ supportedServers isKindOfClass : [ NSArray class ] ] ) {
idx = [ supportedServers count ] ;
while ( ( NO = = ret ) && ( - - idx >= 0 ) ) {
curServer = [ supportedServers objectAtIndex : idx ] ;
ret = [ curServer isEqualToString : name ] ;
}
} else ret = [ curServer isEqualToString : name ] ;
}
return ret ;
}
NSString * FindLinkBackServer ( NSString * bundleIdentifier , NSString * serverName , NSString * dir , int level )
{
NSString * ret = nil ;
NSFileManager * fm = [ NSFileManager defaultManager ] ;
2011-02-20 11:12:35 +00:00
# if defined ( __MAC _OS _X _VERSION _MAX _ALLOWED ) && ( __MAC _OS _X _VERSION _MAX _ALLOWED >= 1050 )
2010-04-08 22:41:49 +00:00
NSArray * contents = [ fm contentsOfDirectoryAtPath : dir error : nil ] ;
2011-02-20 11:12:35 +00:00
NSUInteger idx ;
2010-04-14 13:22:00 +00:00
# else
NSArray * contents = [ fm directoryContentsAtPath : dir ] ;
2011-02-20 11:12:35 +00:00
int idx ;
2010-04-14 13:22:00 +00:00
# endif
2008-02-03 10:41:23 +00:00
// working info
NSString * cpath ;
NSBundle * cbundle ;
NSString * cbundleIdentifier ;
id supportedServers ;
2010-12-16 08:03:24 +00:00
NSLog ( @ "searching for %@ in folder: %@" , serverName , dir ) ;
2008-02-03 10:41:23 +00:00
// resolve any symlinks , expand tildes .
dir = [ dir stringByStandardizingPath ] ;
// find all . app bundles in the directory and test them .
idx = ( contents ) ? [ contents count ] : 0 ;
while ( ( nil = = ret ) && ( - - idx >= 0 ) ) {
cpath = [ contents objectAtIndex : idx ] ;
if ( [ [ cpath pathExtension ] isEqualToString : @ "app" ] ) {
cpath = [ dir stringByAppendingPathComponent : cpath ] ;
cbundle = [ NSBundle bundleWithPath : cpath ] ;
cbundleIdentifier = [ cbundle bundleIdentifier ] ;
if ( [ cbundleIdentifier isEqualToString : bundleIdentifier ] ) {
supportedServers = [ [ cbundle infoDictionary ] objectForKey : @ "LinkBackServer" ] ;
ret = ( LinkBackServerIsSupported ( serverName , supportedServers ) ) ? cpath : nil ;
}
}
}
// if the app was not found , descend into non - app dirs . only descend 4 levels to avoid taking forever .
if ( ( nil = = ret ) && ( level < 4 ) ) {
idx = ( contents ) ? [ contents count ] : 0 ;
while ( ( nil = = ret ) && ( - - idx >= 0 ) ) {
BOOL isdir ;
cpath = [ contents objectAtIndex : idx ] ;
[ fm fileExistsAtPath : cpath isDirectory : & isdir ] ;
if ( isdir && ( ! [ [ cpath pathExtension ] isEqualToString : @ "app" ] ) ) {
cpath = [ dir stringByAppendingPathComponent : cpath ] ;
ret = FindLinkBackServer ( bundleIdentifier , serverName , cpath , level + 1 ) ;
}
}
}
return ret ;
}
void LinkBackRunAppNotFoundPanel ( NSString * appName , NSURL * url )
{
int result ;
// strings for panel
NSBundle * b = [ NSBundle bundleForClass : [ LinkBack class ] ] ;
NSString * title ;
NSString * msg ;
NSString * ok ;
NSString * urlstr ;
title = NSLocalizedStringFromTableInBundle ( @ "_AppNotFoundTitle" , @ "Localized" , b , @ "app not found title" ) ;
ok = NSLocalizedStringFromTableInBundle ( @ "_OK" , @ "Localized" , b , @ "ok" ) ;
msg = ( url ) ? NSLocalizedStringFromTableInBundle ( @ "_AppNotFoundMessageWithURL" , @ "Localized" , b , @ "app not found msg" ) : NSLocalizedStringFromTableInBundle ( @ "_AppNotFoundMessageNoURL" , @ "Localized" , b , @ "app not found msg" ) ;
urlstr = ( url ) ? NSLocalizedStringFromTableInBundle ( @ "_GetApplication" , @ "Localized" , b , @ "Get application" ) : nil ;
title = [ NSString stringWithFormat : title , appName ] ;
result = NSRunCriticalAlertPanel ( title , msg , ok , urlstr , nil ) ;
if ( NSAlertAlternateReturn = = result ) {
[ [ NSWorkspace sharedWorkspace ] openURL : url ] ;
}
}
2010-12-16 08:03:24 +00:00
+ ( LinkBackServer * ) LinkBackServerWithName : ( NSString * ) aName inApplication : ( NSString * ) bundleIdentifier launchIfNeeded : ( BOOL ) flag fallbackURL : ( NSURL * ) url appName : ( NSString * ) appName
2008-02-03 10:41:23 +00:00
{
BOOL connect = YES ;
NSString * serverName = MakeLinkBackServerName ( bundleIdentifier , aName ) ;
id ret = nil ;
NSTimeInterval tryMark ;
// Try to connect
ret = [ NSConnection rootProxyForConnectionWithRegisteredName : serverName host : nil ] ;
// if launchIfNeeded , and the connection was not available , try to launch .
if ( ( ! ret ) && ( flag ) ) {
NSString * appPath ;
id linkBackServers ;
// first , try to find the app with the bundle identifier
appPath = [ [ NSWorkspace sharedWorkspace ] absolutePathForAppBundleWithIdentifier : bundleIdentifier ] ;
linkBackServers = [ [ [ NSBundle bundleWithPath : appPath ] infoDictionary ] objectForKey : @ "LinkBackServer" ] ;
appPath = ( LinkBackServerIsSupported ( aName , linkBackServers ) ) ? appPath : nil ;
// if the found app is not supported , we will need to search for the app ourselves .
if ( nil = = appPath ) appPath = FindLinkBackServer ( bundleIdentifier , aName , @ "/Applications" , 0 ) ;
if ( nil = = appPath ) appPath = FindLinkBackServer ( bundleIdentifier , aName , @ "~/Applications" , 0 ) ;
if ( nil = = appPath ) appPath = FindLinkBackServer ( bundleIdentifier , aName , @ "/Network/Applications" , 0 ) ;
// if app path has been found , launch the app .
if ( appPath ) {
[ [ NSWorkspace sharedWorkspace ] launchApplication : appName ] ;
} else {
LinkBackRunAppNotFoundPanel ( appName , url ) ;
connect = NO ;
}
}
// if needed , try to connect .
// retry connection for a while if we did not succeed at first . This gives the app time to launch .
if ( connect && ( nil = = ret ) ) {
tryMark = [ NSDate timeIntervalSinceReferenceDate ] ;
do {
ret = [ NSConnection rootProxyForConnectionWithRegisteredName : serverName host : nil ] ;
} while ( ( ! ret ) && ( ( [ NSDate timeIntervalSinceReferenceDate ] - tryMark ) < 10 ) ) ;
}
// setup protocol and return
if ( ret ) [ ret setProtocolForProxy : @ protocol ( LinkBackServer ) ] ;
return ret ;
}
- ( id ) initWithName : ( NSString * ) aName delegate : ( id < LinkBackServerDelegate > ) aDel
{
2010-08-31 13:25:29 +00:00
self = [ super init ] ;
if ( self ) {
2008-02-03 10:41:23 +00:00
name = [ aName copy ] ;
delegate = aDel ;
listener = nil ;
}
return self ;
}
- ( void ) dealloc
{
if ( listener ) [ self retract ] ;
[ name release ] ;
[ super dealloc ] ;
}
- ( BOOL ) publish
{
NSString * serverName = MakeLinkBackServerName ( [ [ NSBundle mainBundle ] bundleIdentifier ] , name ) ;
BOOL ret = YES ;
// create listener and connect
NSPort * port = [ NSPort port ] ;
listener = [ NSConnection connectionWithReceivePort : port sendPort : port ] ;
[ listener setRootObject : self ] ;
ret = [ listener registerName : serverName ] ;
// if successful , retain connection and add self to list of servers .
if ( ret ) {
[ listener retain ] ;
[ LinkBackServers setObject : self forKey : name ] ;
} else listener = nil ; // listener will dealloc on its own .
return ret ;
}
- ( void ) retract
{
if ( listener ) {
[ listener invalidate ] ;
[ listener release ] ;
listener = nil ;
}
[ LinkBackServers removeObjectForKey : name ] ;
}
- ( LinkBack * ) initiateLinkBackFromClient : ( LinkBack * ) clientLinkBack
{
LinkBack * ret = [ [ LinkBack alloc ] initServerWithClient : clientLinkBack delegate : delegate ] ;
// NOTE : we do not release because LinkBack will release itself when it the link closes . ( caj )
return ret ;
}
@ end