diff --git a/engine/common/filesystem_engine.c b/engine/common/filesystem_engine.c index 71a0bf18..3bda62d7 100644 --- a/engine/common/filesystem_engine.c +++ b/engine/common/filesystem_engine.c @@ -16,7 +16,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ -#include "xash3d_types.h" #if XASH_SDL == 2 #include // SDL_GetBasePath #elif XASH_SDL == 3 @@ -322,7 +321,7 @@ static qboolean FS_DetermineReadOnlyRootDirectory( char *out, size_t size ) } #if XASH_IOS - Q_strncpy(out, IOS_GetExecDir(), size); + Q_strncpy( out, IOS_GetExecDir(), size ); return true; #endif diff --git a/engine/platform/ios/AsyncSocket.h b/engine/platform/ios/AsyncSocket.h deleted file mode 100644 index 27b51859..00000000 --- a/engine/platform/ios/AsyncSocket.h +++ /dev/null @@ -1,282 +0,0 @@ -// -// AsyncSocket.h -// -// This class is in the public domain. -// Originally created by Dustin Voss on Wed Jan 29 2003. -// Updated and maintained by Deusty Designs and the Mac development community. -// -// http://code.google.com/p/cocoaasyncsocket/ -// - -#import - -@class AsyncSocket; -@class AsyncReadPacket; -@class AsyncWritePacket; - -extern NSString *const AsyncSocketException; -extern NSString *const AsyncSocketErrorDomain; - -enum AsyncSocketError -{ - AsyncSocketCFSocketError = kCFSocketError, // From CFSocketError enum. - AsyncSocketNoError = 0, // Never used. - AsyncSocketCanceledError, // onSocketWillConnect: returned NO. - AsyncSocketReadMaxedOutError, // Reached set maxLength without completing - AsyncSocketReadTimeoutError, - AsyncSocketWriteTimeoutError -}; -typedef enum AsyncSocketError AsyncSocketError; - -@interface NSObject (AsyncSocketDelegate) - -/** - * In the event of an error, the socket is closed. - * You may call "unreadData" during this call-back to get the last bit of data off the socket. - * When connecting, this delegate method may be called - * before"onSocket:didAcceptNewSocket:" or "onSocket:didConnectToHost:". - **/ -- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err; - -/** - * Called when a socket disconnects with or without error. If you want to release a socket after it disconnects, - * do so here. It is not safe to do that during "onSocket:willDisconnectWithError:". - **/ -- (void)onSocketDidDisconnect:(AsyncSocket *)sock; - -/** - * Called when a socket accepts a connection. Another socket is spawned to handle it. The new socket will have - * the same delegate and will call "onSocket:didConnectToHost:port:". - **/ -- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket; - -/** - * Called when a new socket is spawned to handle a connection. This method should return the run-loop of the - * thread on which the new socket and its delegate should operate. If omitted, [NSRunLoop currentRunLoop] is used. - **/ -- (NSRunLoop *)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket; - -/** - * Called when a socket is about to connect. This method should return YES to continue, or NO to abort. - * If aborted, will result in AsyncSocketCanceledError. - * - * If the connectToHost:onPort:error: method was called, the delegate will be able to access and configure the - * CFReadStream and CFWriteStream as desired prior to connection. - * - * If the connectToAddress:error: method was called, the delegate will be able to access and configure the - * CFSocket and CFSocketNativeHandle (BSD socket) as desired prior to connection. You will be able to access and - * configure the CFReadStream and CFWriteStream in the onSocket:didConnectToHost:port: method. - **/ -- (BOOL)onSocketWillConnect:(AsyncSocket *)sock; - - -/** - * Called when a read stream is closed. This method should return YES, if you - * want to close the socket of the stream, or NO then the socket won't be closed. - * This was added for the workaround to solve the issue of firefox and os-x finder, - * which seem to close a read stream for ftp's list command. - * 'iosftpserver' returns YES only when the socket is for FTP::STORE command. - **/ -- (BOOL)onReadStreamEnded:(AsyncSocket *)sock; - -/** - * Called when a socket connects and is ready for reading and writing. - * The host parameter will be an IP address, not a DNS name. - **/ -- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port; - -/** - * Called when a socket has completed reading the requested data into memory. - * Not called if there is an error. - **/ -- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag; - -/** - * Called when a socket has read in data, but has not yet completed the read. - * This would occur if using readToData: or readToLength: methods. - * It may be used to for things such as updating progress bars. - **/ -- (void)onSocket:(AsyncSocket *)sock didReadPartialDataOfLength:(CFIndex)partialLength tag:(long)tag; - -/** - * Called when a socket has completed writing the requested data. Not called if there is an error. - **/ -- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag; - -@end - -@interface AsyncSocket : NSObject -{ - CFSocketRef theSocket; // IPv4 accept or connect socket - CFSocketRef theSocket6; // IPv6 accept or connect socket - CFReadStreamRef theReadStream; - CFWriteStreamRef theWriteStream; - - CFRunLoopSourceRef theSource; // For theSocket - CFRunLoopSourceRef theSource6; // For theSocket6 - CFRunLoopRef theRunLoop; - CFSocketContext theContext; - - NSMutableArray *theReadQueue; - AsyncReadPacket *theCurrentRead; - NSTimer *theReadTimer; - NSMutableData *partialReadBuffer; - - NSMutableArray *theWriteQueue; - AsyncWritePacket *theCurrentWrite; - NSTimer *theWriteTimer; - - id theDelegate; - Byte theFlags; - - long theUserData; -} - -- (id)init; -- (id)initWithDelegate:(id)delegate; -- (id)initWithDelegate:(id)delegate userData:(long)userData; - -/* String representation is long but has no "\n". */ -- (NSString *)description; - -/** - * Use "canSafelySetDelegate" to see if there is any pending business (reads and writes) with the current delegate - * before changing it. It is, of course, safe to change the delegate before connecting or accepting connections. - **/ -- (id)delegate; -- (BOOL)canSafelySetDelegate; -- (void)setDelegate:(id)delegate; - -/* User data can be a long, or an id or void * cast to a long. */ -- (long)userData; -- (void)setUserData:(long)userData; - -/* Don't use these to read or write. And don't close them, either! */ -- (CFSocketRef)getCFSocket; -- (CFReadStreamRef)getCFReadStream; -- (CFWriteStreamRef)getCFWriteStream; - -/** - * Once one of these methods is called, the AsyncSocket instance is locked in, and the rest can't be called without - * disconnecting the socket first. If the attempt times out or fails, these methods either return NO or - * call "onSocket:willDisconnectWithError:" and "onSockedDidDisconnect:". - **/ -- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr; -- (BOOL)acceptOnAddress:(NSString *)hostaddr port:(UInt16)port error:(NSError **)errPtr; -- (BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr; -- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; - -/** - * Disconnects immediately. Any pending reads or writes are dropped. - **/ -- (void)disconnect; - -/** - * Disconnects after all pending writes have completed. - * After calling this, the read and write methods (including "readDataWithTimeout:tag:") will do nothing. - * The socket will disconnect even if there are still pending reads. - **/ -- (void)disconnectAfterWriting; - -/* Returns YES if the socket and streams are open, connected, and ready for reading and writing. */ -- (BOOL)isConnected; - -/** - * Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected. - * The host will be an IP address. - **/ -- (NSString *)connectedHost; -- (UInt16)connectedPort; - -- (NSString *)localHost; -- (UInt16)localPort; - -- (BOOL)isIPv4; -- (BOOL)isIPv6; - -// The readData and writeData methods won't block. To not time out, use a negative time interval. -// If they time out, "onSocket:disconnectWithError:" is called. The tag is for your convenience. -// You can use it as an array index, step number, state id, pointer, etc., just like the socket's user data. - -/** - * This will read a certain number of bytes into memory, and call the delegate method when those bytes have been read. - * If there is an error, partially read data is lost. - * If the length is 0, this method does nothing and the delegate is not called. - **/ -- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag; - -/** - * This reads bytes until (and including) the passed "data" parameter, which acts as a separator. - * The bytes and the separator are returned by the delegate method. - * - * If you pass nil or zero-length data as the "data" parameter, - * the method will do nothing, and the delegate will not be called. - * - * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. - * Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for - * a character, the read will prematurely end. - **/ -- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; - -/** - * Same as readDataToData:withTimeout:tag, with the additional restriction that the amount of data read - * may not surpass the given maxLength (specified in bytes). - * - * If you pass a maxLength parameter that is less than the length of the data parameter, - * the method will do nothing, and the delegate will not be called. - * - * If the max length is surpassed, it is treated the same as a timeout - the socket is closed. - * - * Pass -1 as maxLength if no length restriction is desired, or simply use the readDataToData:withTimeout:tag method. - **/ -- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag; - -/** - * Reads the first available bytes that become available on the socket. - **/ -- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag; - -/* Writes data to the socket, and calls the delegate when finished. - * - * If you pass in nil or zero-length data, this method does nothing and the delegate will not be called. - **/ -- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; - -/** - * Returns progress of current read or write, from 0.0 to 1.0, or NaN if no read/write (use isnan() to check). - * "tag", "done" and "total" will be filled in if they aren't NULL. - **/ -- (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total; -- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total; - -/** - * For handling readDataToData requests, data is necessarily read from the socket in small increments. - * The performance can be improved by allowing AsyncSocket to read larger chunks at a time and - * store any overflow in a small internal buffer. - * This is termed pre-buffering, as some data may be read for you before you ask for it. - * If you use readDataToData a lot, enabling pre-buffering may offer a small performance improvement. - * - * Pre-buffering is disabled by default. You must explicitly enable it to turn it on. - * - * Note: If your protocol negotiates upgrades to TLS (as opposed to using TLS from the start), you should - * consider how, if at all, pre-buffering could affect the TLS negotiation sequence. - * This is because TLS runs atop TCP, and requires sending/receiving a TLS handshake over the TCP socket. - * If the negotiation sequence is poorly designed, pre-buffering could potentially pre-read part of the TLS handshake, - * thus causing TLS to fail. In almost all cases, especially when implementing a formalized protocol, this will never - * be a hazard. - **/ -- (void)enablePreBuffering; - -/** - * In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read - * any data that's left on the socket. - **/ -- (NSData *)unreadData; - -/* A few common line separators, for use with "readDataToData:withTimeout:tag:". */ -+ (NSData *)CRLFData; // 0x0D0A -+ (NSData *)CRData; // 0x0D -+ (NSData *)LFData; // 0x0A -+ (NSData *)ZeroData; // 0x00 - -@end diff --git a/engine/platform/ios/AsyncSocket.m b/engine/platform/ios/AsyncSocket.m deleted file mode 100644 index b85a4796..00000000 --- a/engine/platform/ios/AsyncSocket.m +++ /dev/null @@ -1,2426 +0,0 @@ -// -// AsyncSocket.m -// -// This class is in the public domain. -// Originally created by Dustin Voss on Wed Jan 29 2003. -// Updated and maintained by Deusty Designs and the Mac development community. -// -// http://code.google.com/p/cocoaasyncsocket/ -// - -#import "AsyncSocket.h" -#import -#import -#import -#import - -#if TARGET_OS_IPHONE -// Note: You may need to add the CFNetwork Framework to your project -#import -#endif - -#pragma mark Declarations - -#define READQUEUE_CAPACITY 5 // Initial capacity -#define WRITEQUEUE_CAPACITY 5 // Initial capacity -#define READALL_CHUNKSIZE 256 // Incremental increase in buffer size -#define WRITE_CHUNKSIZE (1024 * 4) // Limit on size of each write pass - -NSString *const AsyncSocketException = @"AsyncSocketException"; -NSString *const AsyncSocketErrorDomain = @"AsyncSocketErrorDomain"; - -// This is a mutex lock used by all instances of AsyncSocket, to protect getaddrinfo. -// The man page says it is not thread-safe. (As of Mac OS X 10.4.7, and possibly earlier) -static NSString *getaddrinfoLock = @"lock"; - -enum AsyncSocketFlags -{ - kEnablePreBuffering = 1 << 0, // If set, pre-buffering is enabled. - kDidCallConnectDeleg = 1 << 1, // If set, connect delegate has been called. - kDidPassConnectMethod = 1 << 2, // If set, disconnection results in delegate call. - kForbidReadsWrites = 1 << 3, // If set, no new reads or writes are allowed. - kDisconnectSoon = 1 << 4, // If set, disconnect as soon as nothing is queued. - kClosingWithError = 1 << 5, // If set, the socket is being closed due to an error. -}; - -@interface AsyncSocket (Private) - -// Socket Implementation -- (CFSocketRef) createAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr; -- (BOOL) createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr; -- (BOOL) attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr; -- (BOOL) configureSocketAndReturnError:(NSError **)errPtr; -- (BOOL) connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; -- (void) doAcceptWithSocket:(CFSocketNativeHandle)newSocket; -- (void) doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)err; - -// Stream Implementation -- (BOOL) createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr; -- (BOOL) createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr; -- (BOOL) attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr; -- (BOOL) configureStreamsAndReturnError:(NSError **)errPtr; -- (BOOL) openStreamsAndReturnError:(NSError **)errPtr; -- (void) doStreamOpen; -- (BOOL) setSocketFromStreamsAndReturnError:(NSError **)errPtr; - -// Disconnect Implementation -- (void) closeWithError:(NSError *)err; -- (void) recoverUnreadData; -- (void) emptyQueues; -- (void) close; - -// Errors -- (NSError *) getErrnoError; -- (NSError *) getAbortError; -- (NSError *) getStreamError; -- (NSError *) getSocketError; -- (NSError *) getReadMaxedOutError; -- (NSError *) getReadTimeoutError; -- (NSError *) getWriteTimeoutError; -- (NSError *) errorFromCFStreamError:(CFStreamError)err; - -// Diagnostics -- (BOOL) isSocketConnected; -- (BOOL) areStreamsConnected; -- (NSString *) connectedHost: (CFSocketRef)socket; -- (UInt16) connectedPort: (CFSocketRef)socket; -- (NSString *) localHost: (CFSocketRef)socket; -- (UInt16) localPort: (CFSocketRef)socket; -- (NSString *) addressHost: (CFDataRef)cfaddr; -- (UInt16) addressPort: (CFDataRef)cfaddr; - -// Reading -- (void) doBytesAvailable; -- (void) completeCurrentRead; -- (void) endCurrentRead; -- (void) scheduleDequeueRead; -- (void) maybeDequeueRead; -- (void) doReadTimeout:(NSTimer *)timer; - -// Writing -- (void) doSendBytes; -- (void) completeCurrentWrite; -- (void) endCurrentWrite; -- (void) scheduleDequeueWrite; -- (void) maybeDequeueWrite; -- (void) maybeScheduleDisconnect; -- (void) doWriteTimeout:(NSTimer *)timer; - -// Callbacks -- (void) doCFCallback:(CFSocketCallBackType)type forSocket:(CFSocketRef)sock withAddress:(NSData *)address withData:(const void *)pData; -- (void) doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream; -- (void) doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream; - -@end - -static void MyCFSocketCallback (CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *); -static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo); -static void MyCFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo); - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * The AsyncReadPacket encompasses the instructions for a current read. - * The content of a read packet allows the code to determine if we're: - * reading to a certain length, reading to a certain separator, or simply reading the first chunk of data. - **/ -@interface AsyncReadPacket : NSObject -{ -@public - NSMutableData *buffer; - CFIndex bytesDone; - NSTimeInterval timeout; - CFIndex maxLength; - long tag; - NSData *term; - BOOL readAllAvailableData; -} -- (id)initWithData:(NSMutableData *)d - timeout:(NSTimeInterval)t - tag:(long)i - readAllAvailable:(BOOL)a - terminator:(NSData *)e - maxLength:(CFIndex)m; - -- (unsigned)readLengthForTerm; - -- (unsigned)prebufferReadLengthForTerm; -- (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes; - -- (void)dealloc; -@end - -@implementation AsyncReadPacket - -- (id)initWithData:(NSMutableData *)d - timeout:(NSTimeInterval)t - tag:(long)i - readAllAvailable:(BOOL)a - terminator:(NSData *)e - maxLength:(CFIndex)m -{ - if(self = [super init]) - { - buffer = [d retain]; - timeout = t; - tag = i; - readAllAvailableData = a; - term = [e copy]; - bytesDone = 0; - maxLength = m; - } - return self; -} - -/** - * For read packets with a set terminator, returns the safe length of data that can be read - * without going over a terminator, or the maxLength. - * - * It is assumed the terminator has not already been read. - **/ -- (unsigned)readLengthForTerm -{ - NSAssert(term != nil, @"Searching for term in data when there is no term."); - - // What we're going to do is look for a partial sequence of the terminator at the end of the buffer. - // If a partial sequence occurs, then we must assume the next bytes to arrive will be the rest of the term, - // and we can only read that amount. - // Otherwise, we're safe to read the entire length of the term. - - unsigned result = [term length]; - - // i = index within buffer at which to check data - // j = length of term to check against - - // Note: Beware of implicit casting rules - // This could give you -1: MAX(0, (0 - [term length] + 1)); - - CFIndex i = MAX(0, (CFIndex)(bytesDone - [term length] + 1)); - CFIndex j = MIN([term length] - 1, bytesDone); - - while(i < bytesDone) - { - const void *subBuffer = [buffer bytes] + i; - - if(memcmp(subBuffer, [term bytes], j) == 0) - { - result = [term length] - j; - break; - } - - i++; - j--; - } - - if(maxLength > 0) - return MIN(result, (maxLength - bytesDone)); - else - return result; -} - -/** - * Assuming pre-buffering is enabled, returns the amount of data that can be read - * without going over the maxLength. - **/ -- (unsigned)prebufferReadLengthForTerm -{ - if(maxLength > 0) - return MIN(READALL_CHUNKSIZE, (maxLength - bytesDone)); - else - return READALL_CHUNKSIZE; -} - -/** - * For read packets with a set terminator, scans the packet buffer for the term. - * It is assumed the terminator had not been fully read prior to the new bytes. - * - * If the term is found, the number of excess bytes after the term are returned. - * If the term is not found, this method will return -1. - * - * Note: A return value of zero means the term was found at the very end. - **/ -- (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes -{ - NSAssert(term != nil, @"Searching for term in data when there is no term."); - - // We try to start the search such that the first new byte read matches up with the last byte of the term. - // We continue searching forward after this until the term no longer fits into the buffer. - - // Note: Beware of implicit casting rules - // This could give you -1: MAX(0, 1 - 1 - [term length] + 1); - - CFIndex i = MAX(0, (CFIndex)(bytesDone - numBytes - [term length] + 1)); - - while(i + [term length] <= bytesDone) - { - const void *subBuffer = [buffer bytes] + i; - - if(memcmp(subBuffer, [term bytes], [term length]) == 0) - { - return bytesDone - (i + [term length]); - } - - i++; - } - - return -1; -} - -- (void)dealloc -{ - [buffer release]; - [term release]; - [super dealloc]; -} - -@end - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -@interface AsyncWritePacket : NSObject -{ -@public - NSData *buffer; - CFIndex bytesDone; - long tag; - NSTimeInterval timeout; -} -- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i; -- (void)dealloc; -@end - -@implementation AsyncWritePacket - -- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i; -{ - if(self = [super init]) - { - buffer = [d retain]; - timeout = t; - tag = i; - bytesDone = 0; - } - return self; -} - -- (void)dealloc -{ - [buffer release]; - [super dealloc]; -} - -@end - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -@implementation AsyncSocket - -- (id)init -{ - return [self initWithDelegate:nil userData:0]; -} - -- (id)initWithDelegate:(id)delegate -{ - return [self initWithDelegate:delegate userData:0]; -} - -// Designated initializer. -- (id)initWithDelegate:(id)delegate userData:(long)userData -{ - if(self = [super init]) - { - theFlags = 0x00; - theDelegate = delegate; - theUserData = userData; - - theSocket = NULL; - theSource = NULL; - theSocket6 = NULL; - theSource6 = NULL; - theRunLoop = NULL; - theReadStream = NULL; - theWriteStream = NULL; - - theReadQueue = [[NSMutableArray alloc] initWithCapacity:READQUEUE_CAPACITY]; - theCurrentRead = nil; - theReadTimer = nil; - - partialReadBuffer = [[NSMutableData alloc] initWithCapacity:READALL_CHUNKSIZE]; - - theWriteQueue = [[NSMutableArray alloc] initWithCapacity:WRITEQUEUE_CAPACITY]; - theCurrentWrite = nil; - theWriteTimer = nil; - - // Socket context - NSAssert(sizeof(CFSocketContext) == sizeof(CFStreamClientContext), @"CFSocketContext != CFStreamClientContext"); - theContext.version = 0; - theContext.info = self; - theContext.retain = nil; - theContext.release = nil; - theContext.copyDescription = nil; - } - return self; -} - -// The socket may been initialized in a connected state and auto-released, so this should close it down cleanly. -- (void)dealloc -{ - [self close]; - [theReadQueue release]; - [theWriteQueue release]; - [NSObject cancelPreviousPerformRequestsWithTarget:theDelegate selector:@selector(onSocketDidDisconnect:) object:self]; - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - [super dealloc]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Accessors -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (long)userData -{ - return theUserData; -} - -- (void)setUserData:(long)userData -{ - theUserData = userData; -} - -- (id)delegate -{ - return theDelegate; -} - -- (void)setDelegate:(id)delegate -{ - theDelegate = delegate; -} - -- (BOOL)canSafelySetDelegate -{ - return ([theReadQueue count] == 0 && [theWriteQueue count] == 0 && theCurrentRead == nil && theCurrentWrite == nil); -} - -- (CFSocketRef)getCFSocket -{ - if(theSocket) - return theSocket; - else - return theSocket6; -} - -- (CFReadStreamRef)getCFReadStream -{ - return theReadStream; -} - -- (CFWriteStreamRef)getCFWriteStream -{ - return theWriteStream; -} - -- (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total -{ - // Check to make sure we're actually reading something right now - if (!theCurrentRead) return NAN; - - // It's only possible to know the progress of our read if we're reading to a certain length - // If we're reading to data, we of course have no idea when the data will arrive - // If we're reading to timeout, then we have no idea when the next chunk of data will arrive. - BOOL hasTotal = (theCurrentRead->readAllAvailableData == NO && theCurrentRead->term == nil); - - CFIndex d = theCurrentRead->bytesDone; - CFIndex t = hasTotal ? [theCurrentRead->buffer length] : 0; - if (tag != NULL) *tag = theCurrentRead->tag; - if (done != NULL) *done = d; - if (total != NULL) *total = t; - float ratio = (float)d/(float)t; - return isnan(ratio) ? 1.0 : ratio; // 0 of 0 bytes is 100% done. -} - -- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total -{ - if (!theCurrentWrite) return NAN; - CFIndex d = theCurrentWrite->bytesDone; - CFIndex t = [theCurrentWrite->buffer length]; - if (tag != NULL) *tag = theCurrentWrite->tag; - if (done != NULL) *done = d; - if (total != NULL) *total = t; - return (float)d/(float)t; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Configuration -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * See the header file for a full explanation of pre-buffering. - **/ -- (void)enablePreBuffering -{ - theFlags |= kEnablePreBuffering; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Connection -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr -{ - return [self acceptOnAddress:nil port:port error:errPtr]; -} - -/** - * To accept on a certain address, pass the address to accept on. - * To accept on any address, pass nil or an empty string. - * To accept only connections from localhost pass "localhost" or "loopback". - **/ -- (BOOL)acceptOnAddress:(NSString *)hostaddr port:(UInt16)port error:(NSError **)errPtr -{ - if (theDelegate == NULL) - [NSException raise:AsyncSocketException format:@"Attempting to accept without a delegate. Set a delegate first."]; - - if (theSocket != NULL || theSocket6 != NULL) - [NSException raise:AsyncSocketException format:@"Attempting to accept while connected or accepting connections. Disconnect first."]; - - // Set up the listen sockaddr structs if needed. - - NSData *address = nil, *address6 = nil; - if(hostaddr == nil || ([hostaddr length] == 0)) - { - // Accept on ANY address - struct sockaddr_in nativeAddr; - nativeAddr.sin_len = sizeof(struct sockaddr_in); - nativeAddr.sin_family = AF_INET; - nativeAddr.sin_port = htons(port); - nativeAddr.sin_addr.s_addr = htonl(INADDR_ANY); - memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero)); - - struct sockaddr_in6 nativeAddr6; - nativeAddr6.sin6_len = sizeof(struct sockaddr_in6); - nativeAddr6.sin6_family = AF_INET6; - nativeAddr6.sin6_port = htons(port); - nativeAddr6.sin6_flowinfo = 0; - nativeAddr6.sin6_addr = in6addr_any; - nativeAddr6.sin6_scope_id = 0; - - // Wrap the native address structures for CFSocketSetAddress. - address = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)]; - address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; - } - else if([hostaddr isEqualToString:@"localhost"] || [hostaddr isEqualToString:@"loopback"]) - { - // Accept only on LOOPBACK address - struct sockaddr_in nativeAddr; - nativeAddr.sin_len = sizeof(struct sockaddr_in); - nativeAddr.sin_family = AF_INET; - nativeAddr.sin_port = htons(port); - nativeAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero)); - - struct sockaddr_in6 nativeAddr6; - nativeAddr6.sin6_len = sizeof(struct sockaddr_in6); - nativeAddr6.sin6_family = AF_INET6; - nativeAddr6.sin6_port = htons(port); - nativeAddr6.sin6_flowinfo = 0; - nativeAddr6.sin6_addr = in6addr_loopback; - nativeAddr6.sin6_scope_id = 0; - - // Wrap the native address structures for CFSocketSetAddress. - address = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)]; - address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; - } - else - { - NSString *portStr = [NSString stringWithFormat:@"%hu", port]; - - @synchronized (getaddrinfoLock) - { - struct addrinfo hints, *res, *res0; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; - - int error = getaddrinfo([hostaddr UTF8String], [portStr UTF8String], &hints, &res0); - - if(error) - { - if(errPtr) - { - NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding]; - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info]; - } - } - - for(res = res0; res; res = res->ai_next) - { - if(!address && (res->ai_family == AF_INET)) - { - // Found IPv4 address - // Wrap the native address structures for CFSocketSetAddress. - address = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; - } - else if(!address6 && (res->ai_family == AF_INET6)) - { - // Found IPv6 address - // Wrap the native address structures for CFSocketSetAddress. - address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; - } - } - freeaddrinfo(res0); - } - - if(!address && !address6) return NO; - } - - // Create the sockets. - - if (address) - { - theSocket = [self createAcceptSocketForAddress:address error:errPtr]; - if (theSocket == NULL) goto Failed; - } - - if (address6) - { - theSocket6 = [self createAcceptSocketForAddress:address6 error:errPtr]; - - // Note: The iPhone doesn't currently support IPv6 - -#if !TARGET_OS_IPHONE - if (theSocket6 == NULL) goto Failed; -#endif - } - - // Attach the sockets to the run loop so that callback methods work - - [self attachSocketsToRunLoop:nil error:nil]; - - // Set the SO_REUSEADDR flags. - - int reuseOn = 1; - if (theSocket) setsockopt(CFSocketGetNative(theSocket), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); - if (theSocket6) setsockopt(CFSocketGetNative(theSocket6), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); - - // Set the local bindings which causes the sockets to start listening. - - CFSocketError err; - if (theSocket) - { - err = CFSocketSetAddress (theSocket, (CFDataRef)address); - if (err != kCFSocketSuccess) goto Failed; - - //NSLog(@"theSocket4: %hu", [self localPort:theSocket]); - } - - if(port == 0 && theSocket && theSocket6) - { - // The user has passed in port 0, which means he wants to allow the kernel to choose the port for them - // However, the kernel will choose a different port for both theSocket and theSocket6 - // So we grab the port the kernel choose for theSocket, and set it as the port for theSocket6 - UInt16 chosenPort = [self localPort:theSocket]; - - struct sockaddr_in6 *pSockAddr6 = (struct sockaddr_in6 *)[address6 bytes]; - pSockAddr6->sin6_port = htons(chosenPort); - } - - if (theSocket6) - { - err = CFSocketSetAddress (theSocket6, (CFDataRef)address6); - if (err != kCFSocketSuccess) goto Failed; - - //NSLog(@"theSocket6: %hu", [self localPort:theSocket6]); - } - - theFlags |= kDidPassConnectMethod; - return YES; - -Failed:; - if(errPtr) *errPtr = [self getSocketError]; - if(theSocket != NULL) - { - CFSocketInvalidate(theSocket); - CFRelease(theSocket); - theSocket = NULL; - } - if(theSocket6 != NULL) - { - CFSocketInvalidate(theSocket6); - CFRelease(theSocket6); - theSocket6 = NULL; - } - return NO; -} - -/** - * This method creates an initial CFReadStream and CFWriteStream to the given host on the given port. - * The connection is then opened, and the corresponding CFSocket will be extracted after the connection succeeds. - * - * Thus the delegate will have access to the CFReadStream and CFWriteStream prior to connection, - * specifically in the onSocketWillConnect: method. - **/ -- (BOOL)connectToHost:(NSString*)hostname onPort:(UInt16)port error:(NSError **)errPtr -{ - if(theDelegate == NULL) - { - NSString *message = @"Attempting to connect without a delegate. Set a delegate first."; - [NSException raise:AsyncSocketException format:@"%@", message]; - } - - if(theSocket != NULL || theSocket6 != NULL) - { - NSString *message = @"Attempting to connect while connected or accepting connections. Disconnect first."; - [NSException raise:AsyncSocketException format:@"%@", message]; - } - - BOOL pass = YES; - - if(pass && ![self createStreamsToHost:hostname onPort:port error:errPtr]) pass = NO; - if(pass && ![self attachStreamsToRunLoop:nil error:errPtr]) pass = NO; - if(pass && ![self configureStreamsAndReturnError:errPtr]) pass = NO; - if(pass && ![self openStreamsAndReturnError:errPtr]) pass = NO; - - if(pass) - theFlags |= kDidPassConnectMethod; - else - [self close]; - - return pass; -} - -/** - * This method creates an initial CFSocket to the given address. - * The connection is then opened, and the corresponding CFReadStream and CFWriteStream will be - * created from the low-level sockets after the connection succeeds. - * - * Thus the delegate will have access to the CFSocket and CFSocketNativeHandle (BSD socket) prior to connection, - * specifically in the onSocketWillConnect: method. - * - * Note: The NSData parameter is expected to be a sockaddr structure. For example, an NSData object returned from - * NSNetservice addresses method. - * If you have an existing struct sockaddr you can convert it to an NSData object like so: - * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; - * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; - **/ -- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr -{ - if (theDelegate == NULL) - { - NSString *message = @"Attempting to connect without a delegate. Set a delegate first."; - [NSException raise:AsyncSocketException format:@"%@", message]; - } - - if (theSocket != NULL || theSocket6 != NULL) - { - NSString *message = @"Attempting to connect while connected or accepting connections. Disconnect first."; - [NSException raise:AsyncSocketException format:@"%@", message]; - } - - BOOL pass = YES; - - if(pass && ![self createSocketForAddress:remoteAddr error:errPtr]) pass = NO; - if(pass && ![self attachSocketsToRunLoop:nil error:errPtr]) pass = NO; - if(pass && ![self configureSocketAndReturnError:errPtr]) pass = NO; - if(pass && ![self connectSocketToAddress:remoteAddr error:errPtr]) pass = NO; - - if(pass) - theFlags |= kDidPassConnectMethod; - else - [self close]; - - return pass; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Socket Implementation: -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Creates the accept sockets. - * Returns true if either IPv4 or IPv6 is created. - * If either is missing, an error is returned (even though the method may return true). - **/ -- (CFSocketRef)createAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr -{ - struct sockaddr *pSockAddr = (struct sockaddr *)[addr bytes]; - int addressFamily = pSockAddr->sa_family; - - CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, - addressFamily, - SOCK_STREAM, - 0, - kCFSocketAcceptCallBack, // Callback flags - (CFSocketCallBack)&MyCFSocketCallback, // Callback method - &theContext); - - if(socket == NULL) - { - if(errPtr) *errPtr = [self getSocketError]; - } - - return socket; -} - -- (BOOL)createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr -{ - struct sockaddr *pSockAddr = (struct sockaddr *)[remoteAddr bytes]; - - if(pSockAddr->sa_family == AF_INET) - { - theSocket = CFSocketCreate(NULL, // Default allocator - PF_INET, // Protocol Family - SOCK_STREAM, // Socket Type - IPPROTO_TCP, // Protocol - kCFSocketConnectCallBack, // Callback flags - (CFSocketCallBack)&MyCFSocketCallback, // Callback method - &theContext); // Socket Context - - if(theSocket == NULL) - { - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - } - else if(pSockAddr->sa_family == AF_INET6) - { - theSocket6 = CFSocketCreate(NULL, // Default allocator - PF_INET6, // Protocol Family - SOCK_STREAM, // Socket Type - IPPROTO_TCP, // Protocol - kCFSocketConnectCallBack, // Callback flags - (CFSocketCallBack)&MyCFSocketCallback, // Callback method - &theContext); // Socket Context - - if(theSocket6 == NULL) - { - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - } - else - { - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - - return YES; -} - -/** - * Adds the CFSocket's to the run-loop so that callbacks will work properly. - **/ -- (BOOL)attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr -{ - // Get the CFRunLoop to which the socket should be attached. - theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop]; - - if(theSocket) - { - theSource = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket, 0); - CFRunLoopAddSource (theRunLoop, theSource, kCFRunLoopDefaultMode); - } - - if(theSocket6) - { - theSource6 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket6, 0); - CFRunLoopAddSource (theRunLoop, theSource6, kCFRunLoopDefaultMode); - } - - return YES; -} - -/** - * Allows the delegate method to configure the CFSocket or CFNativeSocket as desired before we connect. - * Note that the CFReadStream and CFWriteStream will not be available until after the connection is opened. - **/ -- (BOOL)configureSocketAndReturnError:(NSError **)errPtr -{ - // Call the delegate method for further configuration. - if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)]) - { - if([theDelegate onSocketWillConnect:self] == NO) - { - if (errPtr) *errPtr = [self getAbortError]; - return NO; - } - } - return YES; -} - -- (BOOL)connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr -{ - // Start connecting to the given address in the background - // The MyCFSocketCallback method will be called when the connection succeeds or fails - if(theSocket) - { - CFSocketError err = CFSocketConnectToAddress(theSocket, (CFDataRef)remoteAddr, -1); - if(err != kCFSocketSuccess) - { - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - } - else if(theSocket6) - { - CFSocketError err = CFSocketConnectToAddress(theSocket6, (CFDataRef)remoteAddr, -1); - if(err != kCFSocketSuccess) - { - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - } - - return YES; -} - -/** - * Attempt to make the new socket. - * If an error occurs, ignore this event. - **/ -- (void)doAcceptWithSocket:(CFSocketNativeHandle)newNative -{ - AsyncSocket *newSocket = [[[[self class] alloc] initWithDelegate:theDelegate] autorelease]; - - // Note: We use [self class] to support subclassing AsyncSocket. - - if(newSocket) - { - if ([theDelegate respondsToSelector:@selector(onSocket:didAcceptNewSocket:)]) - [theDelegate onSocket:self didAcceptNewSocket:newSocket]; - - NSRunLoop *runLoop = nil; - if ([theDelegate respondsToSelector:@selector(onSocket:wantsRunLoopForNewSocket:)]) - runLoop = [theDelegate onSocket:self wantsRunLoopForNewSocket:newSocket]; - - BOOL pass = YES; - - if(pass && ![newSocket createStreamsFromNative:newNative error:nil]) pass = NO; - if(pass && ![newSocket attachStreamsToRunLoop:runLoop error:nil]) pass = NO; - if(pass && ![newSocket configureStreamsAndReturnError:nil]) pass = NO; - if(pass && ![newSocket openStreamsAndReturnError:nil]) pass = NO; - - if(pass) - newSocket->theFlags |= kDidPassConnectMethod; - else { - // No NSError, but errors will still get logged from the above functions. - [newSocket close]; - } - - } -} - -/** - * Description forthcoming... - **/ -- (void)doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)socketError -{ - NSParameterAssert ((sock == theSocket) || (sock == theSocket6)); - - if(socketError == kCFSocketTimeout || socketError == kCFSocketError) - { - [self closeWithError:[self getSocketError]]; - return; - } - - // Get the underlying native (BSD) socket - CFSocketNativeHandle nativeSocket = CFSocketGetNative(sock); - - // Setup the socket so that invalidating the socket will not close the native socket - CFSocketSetSocketFlags(sock, 0); - - // Invalidate and release the CFSocket - All we need from here on out is the nativeSocket - // Note: If we don't invalidate the socket (leaving the native socket open) - // then theReadStream and theWriteStream won't function properly. - // Specifically, their callbacks won't work, with the exception of kCFStreamEventOpenCompleted. - // I'm not entirely sure why this is, but I'm guessing that events on the socket fire to the CFSocket we created, - // as opposed to the CFReadStream/CFWriteStream. - - CFSocketInvalidate(sock); - CFRelease(sock); - theSocket = NULL; - theSocket6 = NULL; - - NSError *err; - BOOL pass = YES; - - if(pass && ![self createStreamsFromNative:nativeSocket error:&err]) pass = NO; - if(pass && ![self attachStreamsToRunLoop:nil error:&err]) pass = NO; - if(pass && ![self openStreamsAndReturnError:&err]) pass = NO; - - if(!pass) - { - [self closeWithError:err]; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Stream Implementation: -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Creates the CFReadStream and CFWriteStream from the given native socket. - * The CFSocket may be extracted from either stream after the streams have been opened. - * - * Note: The given native socket must already be connected! - **/ -- (BOOL)createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr -{ - // Create the socket & streams. - CFStreamCreatePairWithSocket(kCFAllocatorDefault, native, &theReadStream, &theWriteStream); - if (theReadStream == NULL || theWriteStream == NULL) - { - NSError *err = [self getStreamError]; - NSLog (@"AsyncSocket %p couldn't create streams from accepted socket: %@", self, err); - if (errPtr) *errPtr = err; - return NO; - } - - // Ensure the CF & BSD socket is closed when the streams are closed. - CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - - return YES; -} - -/** - * Creates the CFReadStream and CFWriteStream from the given hostname and port number. - * The CFSocket may be extracted from either stream after the streams have been opened. - **/ -- (BOOL)createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr -{ - // Create the socket & streams. - CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)hostname, port, &theReadStream, &theWriteStream); - if (theReadStream == NULL || theWriteStream == NULL) - { - if (errPtr) *errPtr = [self getStreamError]; - return NO; - } - - // Ensure the CF & BSD socket is closed when the streams are closed. - CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - - return YES; -} - -- (BOOL)attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr -{ - // Get the CFRunLoop to which the socket should be attached. - theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop]; - - // Make read stream non-blocking. - if (!CFReadStreamSetClient (theReadStream, - kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered | kCFStreamEventOpenCompleted, - (CFReadStreamClientCallBack)&MyCFReadStreamCallback, - (CFStreamClientContext *)(&theContext) )) - { - NSError *err = [self getStreamError]; - - NSLog (@"AsyncSocket %p couldn't attach read stream to run-loop,", self); - NSLog (@"Error: %@", err); - - if (errPtr) *errPtr = err; - return NO; - } - CFReadStreamScheduleWithRunLoop (theReadStream, theRunLoop, kCFRunLoopDefaultMode); - - // Make write stream non-blocking. - if (!CFWriteStreamSetClient (theWriteStream, - kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered | kCFStreamEventOpenCompleted, - (CFWriteStreamClientCallBack)&MyCFWriteStreamCallback, - (CFStreamClientContext *)(&theContext) )) - { - NSError *err = [self getStreamError]; - - NSLog (@"AsyncSocket %p couldn't attach write stream to run-loop,", self); - NSLog (@"Error: %@", err); - - if (errPtr) *errPtr = err; - return NO; - - } - CFWriteStreamScheduleWithRunLoop (theWriteStream, theRunLoop, kCFRunLoopDefaultMode); - - return YES; -} - -/** - * Allows the delegate method to configure the CFReadStream and/or CFWriteStream as desired before we connect. - * Note that the CFSocket and CFNativeSocket will not be available until after the connection is opened. - **/ -- (BOOL)configureStreamsAndReturnError:(NSError **)errPtr -{ - // Call the delegate method for further configuration. - if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)]) - { - if([theDelegate onSocketWillConnect:self] == NO) - { - if (errPtr) *errPtr = [self getAbortError]; - return NO; - } - } - return YES; -} - -- (BOOL)openStreamsAndReturnError:(NSError **)errPtr -{ - BOOL pass = YES; - - if(pass && !CFReadStreamOpen (theReadStream)) - { - NSLog (@"AsyncSocket %p couldn't open read stream,", self); - pass = NO; - } - - if(pass && !CFWriteStreamOpen (theWriteStream)) - { - NSLog (@"AsyncSocket %p couldn't open write stream,", self); - pass = NO; - } - - if(!pass) - { - if (errPtr) *errPtr = [self getStreamError]; - } - - return pass; -} - -/** - * Called when read or write streams open. - * When the socket is connected and both streams are open, consider the AsyncSocket instance to be ready. - **/ -- (void)doStreamOpen -{ - NSError *err = nil; - if ([self areStreamsConnected] && !(theFlags & kDidCallConnectDeleg)) - { - // Get the socket. - if (![self setSocketFromStreamsAndReturnError: &err]) - { - NSLog (@"AsyncSocket %p couldn't get socket from streams, %@. Disconnecting.", self, err); - [self closeWithError:err]; - return; - } - - // Call the delegate. - theFlags |= kDidCallConnectDeleg; - if ([theDelegate respondsToSelector:@selector(onSocket:didConnectToHost:port:)]) - { - [theDelegate onSocket:self didConnectToHost:[self connectedHost] port:[self connectedPort]]; - } - - // Immediately deal with any already-queued requests. - [self maybeDequeueRead]; - [self maybeDequeueWrite]; - } -} - -- (BOOL)setSocketFromStreamsAndReturnError:(NSError **)errPtr -{ - // Get the CFSocketNativeHandle from theReadStream - CFSocketNativeHandle native; - CFDataRef nativeProp = CFReadStreamCopyProperty(theReadStream, kCFStreamPropertySocketNativeHandle); - if(nativeProp == NULL) - { - if (errPtr) *errPtr = [self getStreamError]; - return NO; - } - - CFDataGetBytes(nativeProp, CFRangeMake(0, CFDataGetLength(nativeProp)), (UInt8 *)&native); - CFRelease(nativeProp); - - CFSocketRef socket = CFSocketCreateWithNative(kCFAllocatorDefault, native, 0, NULL, NULL); - if(socket == NULL) - { - if (errPtr) *errPtr = [self getSocketError]; - return NO; - } - - // Determine whether the connection was IPv4 or IPv6 - CFDataRef peeraddr = CFSocketCopyPeerAddress(socket); - struct sockaddr *sa = (struct sockaddr *)CFDataGetBytePtr(peeraddr); - - if(sa->sa_family == AF_INET) - { - theSocket = socket; - } - else - { - theSocket6 = socket; - } - - CFRelease(peeraddr); - - return YES; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Disconnect Implementation: -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Sends error message and disconnects -- (void)closeWithError:(NSError *)err -{ - theFlags |= kClosingWithError; - - if (theFlags & kDidPassConnectMethod) - { - // Try to salvage what data we can. - [self recoverUnreadData]; - - // Let the delegate know, so it can try to recover if it likes. - if ([theDelegate respondsToSelector:@selector(onSocket:willDisconnectWithError:)]) - { - [theDelegate onSocket:self willDisconnectWithError:err]; - } - } - [self close]; -} - -// Prepare partially read data for recovery. -- (void)recoverUnreadData -{ - if((theCurrentRead != nil) && (theCurrentRead->bytesDone > 0)) - { - // We never finished the current read. - // We need to move its data into the front of the partial read buffer. - - [partialReadBuffer replaceBytesInRange:NSMakeRange(0, 0) - withBytes:[theCurrentRead->buffer bytes] - length:theCurrentRead->bytesDone]; - } - - [self emptyQueues]; -} - -- (void)emptyQueues -{ - if (theCurrentRead != nil) [self endCurrentRead]; - if (theCurrentWrite != nil) [self endCurrentWrite]; - [theReadQueue removeAllObjects]; - [theWriteQueue removeAllObjects]; - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueRead) object:nil]; - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueWrite) object:nil]; -} - -// Disconnects. This is called for both error and clean disconnections. -- (void)close -{ - // Empty queues. - [self emptyQueues]; - [partialReadBuffer release]; - partialReadBuffer = nil; - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(disconnect) object:nil]; - - // Close streams. - if (theReadStream != NULL) - { - CFReadStreamSetClient(theReadStream, kCFStreamEventNone, NULL, NULL); - CFReadStreamUnscheduleFromRunLoop (theReadStream, theRunLoop, kCFRunLoopDefaultMode); - CFReadStreamClose (theReadStream); - CFRelease (theReadStream); - theReadStream = NULL; - } - if (theWriteStream != NULL) - { - CFWriteStreamSetClient(theWriteStream, kCFStreamEventNone, NULL, NULL); - CFWriteStreamUnscheduleFromRunLoop (theWriteStream, theRunLoop, kCFRunLoopDefaultMode); - CFWriteStreamClose (theWriteStream); - CFRelease (theWriteStream); - theWriteStream = NULL; - } - - // Close sockets. - if (theSocket != NULL) - { - CFSocketInvalidate (theSocket); - CFRelease (theSocket); - theSocket = NULL; - } - if (theSocket6 != NULL) - { - CFSocketInvalidate (theSocket6); - CFRelease (theSocket6); - theSocket6 = NULL; - } - if (theSource != NULL) - { - CFRunLoopRemoveSource (theRunLoop, theSource, kCFRunLoopDefaultMode); - CFRelease (theSource); - theSource = NULL; - } - if (theSource6 != NULL) - { - CFRunLoopRemoveSource (theRunLoop, theSource6, kCFRunLoopDefaultMode); - CFRelease (theSource6); - theSource6 = NULL; - } - theRunLoop = NULL; - - // If the client has passed the connect/accept method, then the connection has at least begun. - // Notify delegate that it is now ending. - if (theFlags & kDidPassConnectMethod) - { - // Delay notification to give him freedom to release without returning here and core-dumping. - if ([theDelegate respondsToSelector: @selector(onSocketDidDisconnect:)]) - { - [theDelegate performSelector:@selector(onSocketDidDisconnect:) withObject:self afterDelay:0]; - } - } - - // Clear flags. - theFlags = 0x00; -} - -/** - * Disconnects immediately. Any pending reads or writes are dropped. - **/ -- (void)disconnect -{ - [self close]; -} - -/** - * Disconnects after all pending writes have completed. - * After calling this, the read and write methods (including "readDataWithTimeout:tag:") will do nothing. - * The socket will disconnect even if there are still pending reads. - **/ -- (void)disconnectAfterWriting -{ - theFlags |= kForbidReadsWrites; - theFlags |= kDisconnectSoon; - [self maybeScheduleDisconnect]; -} - -/** - * In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read - * any data that's left on the socket. - **/ -- (NSData *)unreadData -{ - // Ensure this method will only return data in the event of an error - if(!(theFlags & kClosingWithError)) return nil; - - if(theReadStream == NULL) return nil; - - CFIndex totalBytesRead = [partialReadBuffer length]; - BOOL error = NO; - while(!error && CFReadStreamHasBytesAvailable(theReadStream)) - { - [partialReadBuffer increaseLengthBy:READALL_CHUNKSIZE]; - - // Number of bytes to read is space left in packet buffer. - CFIndex bytesToRead = [partialReadBuffer length] - totalBytesRead; - - // Read data into packet buffer - UInt8 *packetbuf = (UInt8 *)( [partialReadBuffer mutableBytes] + totalBytesRead ); - CFIndex bytesRead = CFReadStreamRead(theReadStream, packetbuf, bytesToRead); - - // Check results - if(bytesRead < 0) - { - error = YES; - } - else - { - totalBytesRead += bytesRead; - } - } - - [partialReadBuffer setLength:totalBytesRead]; - - return partialReadBuffer; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Errors -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Returns a standard error object for the current errno value. - * Errno is used for low-level BSD socket errors. - **/ -- (NSError *)getErrnoError -{ - NSString *errorMsg = [NSString stringWithUTF8String:strerror(errno)]; - NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo]; -} - -/** - * Returns a standard error message for a CFSocket error. - * Unfortunately, CFSocket offers no feedback on its errors. - **/ -- (NSError *)getSocketError -{ - NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCFSocketError", - @"AsyncSocket", [NSBundle mainBundle], - @"General CFSocket error", nil); - - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCFSocketError userInfo:info]; -} - -- (NSError *) getStreamError -{ - CFStreamError err; - if (theReadStream != NULL) - { - err = CFReadStreamGetError (theReadStream); - if (err.error != 0) return [self errorFromCFStreamError: err]; - } - - if (theWriteStream != NULL) - { - err = CFWriteStreamGetError (theWriteStream); - if (err.error != 0) return [self errorFromCFStreamError: err]; - } - - return nil; -} - -/** - * Returns a standard AsyncSocket abort error. - **/ -- (NSError *)getAbortError -{ - NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCanceledError", - @"AsyncSocket", [NSBundle mainBundle], - @"Connection canceled", nil); - - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCanceledError userInfo:info]; -} - -- (NSError *)getReadMaxedOutError -{ - NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadMaxedOutError", - @"AsyncSocket", [NSBundle mainBundle], - @"Read operation reached set maximum length", nil); - - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadMaxedOutError userInfo:info]; -} - -/** - * Returns a standard AsyncSocket read timeout error. - **/ -- (NSError *)getReadTimeoutError -{ - NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadTimeoutError", - @"AsyncSocket", [NSBundle mainBundle], - @"Read operation timed out", nil); - - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadTimeoutError userInfo:info]; -} - -/** - * Returns a standard AsyncSocket write timeout error. - **/ -- (NSError *)getWriteTimeoutError -{ - NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketWriteTimeoutError", - @"AsyncSocket", [NSBundle mainBundle], - @"Write operation timed out", nil); - - NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - - return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketWriteTimeoutError userInfo:info]; -} - -- (NSError *)errorFromCFStreamError:(CFStreamError)err -{ - if (err.domain == 0 && err.error == 0) return nil; - - // Can't use switch; these constants aren't int literals. - NSString *domain = @"CFStreamError (unlisted domain)"; - NSString *message = nil; - - if(err.domain == kCFStreamErrorDomainPOSIX) { - domain = NSPOSIXErrorDomain; - } - else if(err.domain == kCFStreamErrorDomainMacOSStatus) { - domain = NSOSStatusErrorDomain; - } - else if(err.domain == kCFStreamErrorDomainMach) { - domain = NSMachErrorDomain; - } - else if(err.domain == kCFStreamErrorDomainNetDB) - { - domain = @"kCFStreamErrorDomainNetDB"; - message = [NSString stringWithCString:gai_strerror(err.error) encoding:NSASCIIStringEncoding]; - } - else if(err.domain == kCFStreamErrorDomainNetServices) { - domain = @"kCFStreamErrorDomainNetServices"; - } - else if(err.domain == kCFStreamErrorDomainSOCKS) { - domain = @"kCFStreamErrorDomainSOCKS"; - } - else if(err.domain == kCFStreamErrorDomainSystemConfiguration) { - domain = @"kCFStreamErrorDomainSystemConfiguration"; - } - else if(err.domain == kCFStreamErrorDomainSSL) { - domain = @"kCFStreamErrorDomainSSL"; - } - - NSDictionary *info = nil; - if(message != nil) - { - info = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey]; - } - return [NSError errorWithDomain:domain code:err.error userInfo:info]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Diagnostics -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (BOOL)isConnected -{ - return [self isSocketConnected] && [self areStreamsConnected]; -} - -- (NSString *)connectedHost -{ - if(theSocket) - return [self connectedHost:theSocket]; - else - return [self connectedHost:theSocket6]; -} - -- (UInt16)connectedPort -{ - if(theSocket) - return [self connectedPort:theSocket]; - else - return [self connectedPort:theSocket6]; -} - -- (NSString *)localHost -{ - if(theSocket) - return [self localHost:theSocket]; - else - return [self localHost:theSocket6]; -} - -- (UInt16)localPort -{ - if(theSocket) - return [self localPort:theSocket]; - else - return [self localPort:theSocket6]; -} - -- (NSString *)connectedHost:(CFSocketRef)socket -{ - if (socket == NULL) return nil; - CFDataRef peeraddr; - NSString *peerstr = nil; - - if(socket && (peeraddr = CFSocketCopyPeerAddress(socket))) - { - peerstr = [self addressHost:peeraddr]; - CFRelease (peeraddr); - } - - return peerstr; -} - -- (UInt16)connectedPort:(CFSocketRef)socket -{ - if (socket == NULL) return 0; - CFDataRef peeraddr; - UInt16 peerport = 0; - - if(socket && (peeraddr = CFSocketCopyPeerAddress(socket))) - { - peerport = [self addressPort:peeraddr]; - CFRelease (peeraddr); - } - - return peerport; -} - -- (NSString *)localHost:(CFSocketRef)socket -{ - if (socket == NULL) return nil; - CFDataRef selfaddr; - NSString *selfstr = nil; - - if(socket && (selfaddr = CFSocketCopyAddress(socket))) - { - selfstr = [self addressHost:selfaddr]; - CFRelease (selfaddr); - } - - return selfstr; -} - -- (UInt16)localPort:(CFSocketRef)socket -{ - if (socket == NULL) return 0; - CFDataRef selfaddr; - UInt16 selfport = 0; - - if (socket && (selfaddr = CFSocketCopyAddress(socket))) - { - selfport = [self addressPort:selfaddr]; - CFRelease (selfaddr); - } - - return selfport; -} - -- (BOOL)isSocketConnected -{ - if(theSocket != NULL) - return CFSocketIsValid(theSocket); - else if(theSocket6 != NULL) - return CFSocketIsValid(theSocket6); - else - return NO; -} - -- (BOOL)areStreamsConnected -{ - CFStreamStatus s; - - if (theReadStream != NULL) - { - s = CFReadStreamGetStatus (theReadStream); - if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusReading || s == kCFStreamStatusError) ) - return NO; - } - else return NO; - - if (theWriteStream != NULL) - { - s = CFWriteStreamGetStatus (theWriteStream); - if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusWriting || s == kCFStreamStatusError) ) - return NO; - } - else return NO; - - return YES; -} - -- (NSString *)addressHost:(CFDataRef)cfaddr -{ - if (cfaddr == NULL) return nil; - - //char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ]; - char addrBuf[46]; - struct sockaddr *pSockAddr = (struct sockaddr *) CFDataGetBytePtr (cfaddr); - struct sockaddr_in *pSockAddrV4 = (struct sockaddr_in *) pSockAddr; - struct sockaddr_in6 *pSockAddrV6 = (struct sockaddr_in6 *)pSockAddr; - - const void *pAddr = (pSockAddr->sa_family == AF_INET) ? - (void *)(&(pSockAddrV4->sin_addr)) : - (void *)(&(pSockAddrV6->sin6_addr)); - - const char *pStr = inet_ntop (pSockAddr->sa_family, pAddr, addrBuf, sizeof(addrBuf)); - if (pStr == NULL) [NSException raise: NSInternalInconsistencyException - format: @"Cannot convert address to string."]; - - return [NSString stringWithCString:pStr encoding:NSASCIIStringEncoding]; -} - -- (UInt16)addressPort:(CFDataRef)cfaddr -{ - if (cfaddr == NULL) return 0; - struct sockaddr_in *pAddr = (struct sockaddr_in *) CFDataGetBytePtr (cfaddr); - return ntohs (pAddr->sin_port); -} - -- (BOOL)isIPv4 -{ - return (theSocket != NULL); -} - -- (BOOL)isIPv6 -{ - return (theSocket6 != NULL); -} - -- (NSString *)description -{ - static const char *statstr[] = { "not open", "opening", "open", "reading", "writing", "at end", "closed", "has error" }; - CFStreamStatus rs = (theReadStream != NULL) ? CFReadStreamGetStatus (theReadStream) : 0; - CFStreamStatus ws = (theWriteStream != NULL) ? CFWriteStreamGetStatus (theWriteStream) : 0; - NSString *peerstr, *selfstr; - CFDataRef peeraddr = NULL, peeraddr6 = NULL, selfaddr = NULL, selfaddr6 = NULL; - - if (theSocket || theSocket6) - { - if (theSocket) peeraddr = CFSocketCopyPeerAddress(theSocket); - if (theSocket6) peeraddr6 = CFSocketCopyPeerAddress(theSocket6); - - if(theSocket6 && theSocket) - { - peerstr = [NSString stringWithFormat: @"%@/%@ %u", [self addressHost:peeraddr], [self addressHost:peeraddr6], [self addressPort:peeraddr]]; - } - else if(theSocket6) - { - peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr6], [self addressPort:peeraddr6]]; - } - else - { - peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr], [self addressPort:peeraddr]]; - } - - if(peeraddr) CFRelease(peeraddr); - if(peeraddr6) CFRelease(peeraddr6); - peeraddr = NULL; - peeraddr6 = NULL; - } - else peerstr = @"nowhere"; - - if (theSocket || theSocket6) - { - if (theSocket) selfaddr = CFSocketCopyAddress (theSocket); - if (theSocket6) selfaddr6 = CFSocketCopyAddress (theSocket6); - - if (theSocket6 && theSocket) - { - selfstr = [NSString stringWithFormat: @"%@/%@ %u", [self addressHost:selfaddr], [self addressHost:selfaddr6], [self addressPort:selfaddr]]; - } - else if (theSocket6) - { - selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr6], [self addressPort:selfaddr6]]; - } - else - { - selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr], [self addressPort:selfaddr]]; - } - - if(selfaddr) CFRelease(selfaddr); - if(selfaddr6) CFRelease(selfaddr6); - selfaddr = NULL; - selfaddr6 = NULL; - } - else selfstr = @"nowhere"; - - NSMutableString *ms = [[NSMutableString alloc] init]; - [ms appendString: [NSString stringWithFormat:@"buffer length] != 0) - percentDone = (float)theCurrentRead->bytesDone / - (float)[theCurrentRead->buffer length] * 100.0; - else - percentDone = 100; - - [ms appendString: [NSString stringWithFormat:@"currently read %u bytes (%d%% done), ", - [theCurrentRead->buffer length], - theCurrentRead->bytesDone ? percentDone : 0]]; - } - - if (theCurrentWrite == nil) - [ms appendString: @"no current write, "]; - else - { - int percentDone; - if ([theCurrentWrite->buffer length] != 0) - percentDone = (float)theCurrentWrite->bytesDone / - (float)[theCurrentWrite->buffer length] * 100.0; - else - percentDone = 100; - - [ms appendString: [NSString stringWithFormat:@"currently written %u (%d%%), ", - [theCurrentWrite->buffer length], - theCurrentWrite->bytesDone ? percentDone : 0]]; - } - - [ms appendString: [NSString stringWithFormat:@"read stream %p %s, write stream %p %s", theReadStream, statstr [rs], theWriteStream, statstr [ws] ]]; - if (theFlags & kDisconnectSoon) [ms appendString: @", will disconnect soon"]; - if (![self isConnected]) [ms appendString: @", not connected"]; - - [ms appendString: @">"]; - - return [ms autorelease]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Reading -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag; -{ - if(length == 0) return; - if(theFlags & kForbidReadsWrites) return; - - NSMutableData *buffer = [[NSMutableData alloc] initWithLength:length]; - AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer - timeout:timeout - tag:tag - readAllAvailable:NO - terminator:nil - maxLength:length]; - - [theReadQueue addObject:packet]; - [self scheduleDequeueRead]; - - [packet release]; - [buffer release]; -} - -- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag -{ - [self readDataToData:data withTimeout:timeout maxLength:-1 tag:tag]; -} - -- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag -{ - if(data == nil || [data length] == 0) return; - if(length >= 0 && length < [data length]) return; - if(theFlags & kForbidReadsWrites) return; - - NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0]; - AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer - timeout:timeout - tag:tag - readAllAvailable:NO - terminator:data - maxLength:length]; - - [theReadQueue addObject:packet]; - [self scheduleDequeueRead]; - - [packet release]; - [buffer release]; -} - -- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag -{ - if (theFlags & kForbidReadsWrites) return; - - NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0]; - AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer - timeout:timeout - tag:tag - readAllAvailable:YES - terminator:nil - maxLength:-1]; - - [theReadQueue addObject:packet]; - [self scheduleDequeueRead]; - - [packet release]; - [buffer release]; -} - -/** - * Puts a maybeDequeueRead on the run loop. - * An assumption here is that selectors will be performed consecutively within their priority. - **/ -- (void)scheduleDequeueRead -{ - [self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0]; -} - -/** - * This method starts a new read, if needed. - * It is called when a user requests a read, - * or when a stream opens that may have requested reads sitting in the queue, etc. - **/ -- (void)maybeDequeueRead -{ - // If we're not currently processing a read AND - // we have read requests sitting in the queue AND we have actually have a read stream - if(theCurrentRead == nil && [theReadQueue count] != 0 && theReadStream != NULL) - { - // Get new current read AsyncReadPacket. - AsyncReadPacket *newPacket = [theReadQueue objectAtIndex:0]; - theCurrentRead = [newPacket retain]; - [theReadQueue removeObjectAtIndex:0]; - - // Start time-out timer. - if(theCurrentRead->timeout >= 0.0) - { - theReadTimer = [NSTimer scheduledTimerWithTimeInterval:theCurrentRead->timeout - target:self - selector:@selector(doReadTimeout:) - userInfo:nil - repeats:NO]; - } - - // Immediately read, if possible. - [self doBytesAvailable]; - } -} - -/** - * Call this method in doBytesAvailable instead of CFReadStreamHasBytesAvailable(). - * This method supports pre-buffering properly. - **/ -- (BOOL)hasBytesAvailable -{ - return ([partialReadBuffer length] > 0) || CFReadStreamHasBytesAvailable(theReadStream); -} - -/** - * Call this method in doBytesAvailable instead of CFReadStreamRead(). - * This method support pre-buffering properly. - **/ -- (CFIndex)readIntoBuffer:(UInt8 *)buffer maxLength:(CFIndex)length -{ - if([partialReadBuffer length] > 0) - { - // Determine the maximum amount of data to read - CFIndex bytesToRead = MIN(length, [partialReadBuffer length]); - - // Copy the bytes from the buffer - memcpy(buffer, [partialReadBuffer bytes], bytesToRead); - - // Remove the copied bytes from the buffer - [partialReadBuffer replaceBytesInRange:NSMakeRange(0, bytesToRead) withBytes:NULL length:0]; - - return bytesToRead; - } - else - { - return CFReadStreamRead(theReadStream, buffer, length); - } -} - -/** - * This method is called when a new read is taken from the read queue or when new data becomes available on the stream. - **/ -- (void)doBytesAvailable -{ - // If data is available on the stream, but there is no read request, then we don't need to process the data yet. - // Also, if there is a read request, but no read stream setup yet, we can't process any data yet. - if(theCurrentRead != nil && theReadStream != NULL) - { -#if 1 -#define BUFFERSIZE (1024*1024) // the default buffer chunk size - NSMutableData *buffer = [[NSMutableData alloc] initWithLength:BUFFERSIZE]; // buffer - while ( theReadStream && CFReadStreamHasBytesAvailable(theReadStream) ){ - // read 1K and call didReadData - CFIndex readSize = CFReadStreamRead( theReadStream, (UInt8*)[buffer bytes], BUFFERSIZE ); - if ( readSize > 0 ){ - if([theDelegate respondsToSelector:@selector(onSocket:didReadData:withTag:)]) - { - // when delegate is set, pass the current data(up to 1k size) to it. - - NSData *buffer_; - if ( readSize == BUFFERSIZE ){ - // when the buffer size is same as the max size, doesn't create a NSData instance. - buffer_ = buffer; - } - else{ - // create a NSData instance with readSize. - buffer_ = [[NSData alloc] initWithBytesNoCopy: (void*)[buffer bytes] length: readSize freeWhenDone:NO]; - } - [theDelegate onSocket:self didReadData: buffer_ withTag:theCurrentRead->tag]; - if ( readSize < BUFFERSIZE ){ - [buffer_ release]; - } - - } - } - if ( readSize <= 0 ){ - break; - } - } - [buffer release]; -#else - CFIndex totalBytesRead = 0; - - BOOL done = NO; - BOOL socketError = NO; - BOOL maxoutError = NO; - - while(!done && !socketError && !maxoutError && [self hasBytesAvailable]) - { - BOOL didPreBuffer = NO; - - // If reading all available data, make sure there's room in the packet buffer. - if(theCurrentRead->readAllAvailableData == YES) - { - // Make sure there is at least READALL_CHUNKSIZE bytes available. - // We don't want to increase the buffer any more than this or we'll waste space. - // With prebuffering it's possible to read in a small chunk on the first read. - - unsigned buffInc = READALL_CHUNKSIZE - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone); - [theCurrentRead->buffer increaseLengthBy:buffInc]; - } - - // If reading until data, we may only want to read a few bytes. - // Just enough to ensure we don't go past our term or over our max limit. - // Unless pre-buffering is enabled, in which case we may want to read in a larger chunk. - if(theCurrentRead->term != nil) - { - // If we already have data pre-buffered, we obviously don't want to pre-buffer it again. - // So in this case we'll just read as usual. - - if(([partialReadBuffer length] > 0) || !(theFlags & kEnablePreBuffering)) - { - unsigned maxToRead = [theCurrentRead readLengthForTerm]; - - unsigned bufInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone); - [theCurrentRead->buffer increaseLengthBy:bufInc]; - } - else - { - didPreBuffer = YES; - unsigned maxToRead = [theCurrentRead prebufferReadLengthForTerm]; - - unsigned buffInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone); - [theCurrentRead->buffer increaseLengthBy:buffInc]; - - } - } - - // Number of bytes to read is space left in packet buffer. - CFIndex bytesToRead = [theCurrentRead->buffer length] - theCurrentRead->bytesDone; - - // Read data into packet buffer - UInt8 *subBuffer = (UInt8 *)([theCurrentRead->buffer mutableBytes] + theCurrentRead->bytesDone); - CFIndex bytesRead = [self readIntoBuffer:subBuffer maxLength:bytesToRead]; - - // Check results - if(bytesRead < 0) - { - socketError = YES; - } - else - { - // Update total amound read for the current read - theCurrentRead->bytesDone += bytesRead; - - // Update total amount read in this method invocation - totalBytesRead += bytesRead; - } - - // Is packet done? - if(theCurrentRead->readAllAvailableData != YES) - { - if(theCurrentRead->term != nil) - { - if(didPreBuffer) - { - // Search for the terminating sequence within the big chunk we just read. - CFIndex overflow = [theCurrentRead searchForTermAfterPreBuffering:bytesRead]; - - if(overflow > 0) - { - // Copy excess data into partialReadBuffer - NSMutableData *buffer = theCurrentRead->buffer; - const void *overflowBuffer = [buffer bytes] + theCurrentRead->bytesDone - overflow; - - [partialReadBuffer appendBytes:overflowBuffer length:overflow]; - - // Update the bytesDone variable. - // Note: The completeCurrentRead method will trim the buffer for us. - theCurrentRead->bytesDone -= overflow; - } - - done = (overflow >= 0); - } - else - { - // Search for the terminating sequence at the end of the buffer - int termlen = [theCurrentRead->term length]; - if(theCurrentRead->bytesDone >= termlen) - { - const void *buf = [theCurrentRead->buffer bytes] + (theCurrentRead->bytesDone - termlen); - const void *seq = [theCurrentRead->term bytes]; - done = (memcmp (buf, seq, termlen) == 0); - } - } - - if(!done && theCurrentRead->maxLength >= 0 && theCurrentRead->bytesDone >= theCurrentRead->maxLength) - { - // There's a set maxLength, and we've reached that maxLength without completing the read - maxoutError = YES; - } - } - else - { - // Done when (sized) buffer is full. - done = ([theCurrentRead->buffer length] == theCurrentRead->bytesDone); - } - } - // else readAllAvailable doesn't end until all readable is read. - } - - if(theCurrentRead->readAllAvailableData && theCurrentRead->bytesDone > 0) - done = YES; // Ran out of bytes, so the "read-all-data" type packet is done - - if(done) - { - [self completeCurrentRead]; - if (!socketError) [self scheduleDequeueRead]; - } - else if(theCurrentRead->bytesDone > 0) - { - // We're not done with the readToLength or readToData yet, but we have read in some bytes - if ([theDelegate respondsToSelector:@selector(onSocket:didReadPartialDataOfLength:tag:)]) - { - [theDelegate onSocket:self didReadPartialDataOfLength:totalBytesRead tag:theCurrentRead->tag]; - } - } - - if(socketError) - { - CFStreamError err = CFReadStreamGetError(theReadStream); - [self closeWithError:[self errorFromCFStreamError:err]]; - return; - } - if(maxoutError) - { - [self closeWithError:[self getReadMaxedOutError]]; - return; - } -#endif - } -} - -// Ends current read and calls delegate. -- (void)completeCurrentRead -{ - NSAssert (theCurrentRead, @"Trying to complete current read when there is no current read."); - - [theCurrentRead->buffer setLength:theCurrentRead->bytesDone]; - if([theDelegate respondsToSelector:@selector(onSocket:didReadData:withTag:)]) - { - [theDelegate onSocket:self didReadData:theCurrentRead->buffer withTag:theCurrentRead->tag]; - } - - if (theCurrentRead != nil) [self endCurrentRead]; // Caller may have disconnected. -} - -// Ends current read. -- (void)endCurrentRead -{ - NSAssert (theCurrentRead, @"Trying to end current read when there is no current read."); - - [theReadTimer invalidate]; - theReadTimer = nil; - - [theCurrentRead release]; - theCurrentRead = nil; -} - -- (void)doReadTimeout:(NSTimer *)timer -{ - if (timer != theReadTimer) return; // Old timer. Ignore it. - if (theCurrentRead != nil) - { - [self endCurrentRead]; - } - [self closeWithError:[self getReadTimeoutError]]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Writing -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; -{ - if (data == nil || [data length] == 0) return; - if (theFlags & kForbidReadsWrites) return; - - AsyncWritePacket *packet = [[AsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag]; - - [theWriteQueue addObject:packet]; - [self scheduleDequeueWrite]; - - [packet release]; -} - -- (void)scheduleDequeueWrite -{ - [self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0]; -} - -// Start a new write. -- (void)maybeDequeueWrite -{ - if (theCurrentWrite == nil && [theWriteQueue count] != 0 && theWriteStream != NULL) - { - // Get new current write AsyncWritePacket. - AsyncWritePacket *newPacket = [theWriteQueue objectAtIndex:0]; - theCurrentWrite = [newPacket retain]; - [theWriteQueue removeObjectAtIndex:0]; - - // Start time-out timer. - if (theCurrentWrite->timeout >= 0.0) - { - theWriteTimer = [NSTimer scheduledTimerWithTimeInterval:theCurrentWrite->timeout - target:self - selector:@selector(doWriteTimeout:) - userInfo:nil - repeats:NO]; - } - - // Immediately write, if possible. - [self doSendBytes]; - } -} - -- (void)doSendBytes -{ - if (theCurrentWrite != nil && theWriteStream != NULL) - { - BOOL done = NO, error = NO; - while (!done && !error && CFWriteStreamCanAcceptBytes (theWriteStream)) - { - // Figure out what to write. - CFIndex bytesRemaining = [theCurrentWrite->buffer length] - theCurrentWrite->bytesDone; - CFIndex bytesToWrite = (bytesRemaining < WRITE_CHUNKSIZE) ? bytesRemaining : WRITE_CHUNKSIZE; - UInt8 *writestart = (UInt8 *)([theCurrentWrite->buffer bytes] + theCurrentWrite->bytesDone); - - // Write. - CFIndex bytesWritten = CFWriteStreamWrite (theWriteStream, writestart, bytesToWrite); - - // Check results. - if (bytesWritten < 0) - { - bytesWritten = 0; - error = YES; - } - - // Is packet done? - theCurrentWrite->bytesDone += bytesWritten; - done = ([theCurrentWrite->buffer length] == theCurrentWrite->bytesDone); - } - - if(done) - { - [self completeCurrentWrite]; - if (!error) [self scheduleDequeueWrite]; - } - - if(error) - { - CFStreamError err = CFWriteStreamGetError (theWriteStream); - [self closeWithError: [self errorFromCFStreamError:err]]; - return; - } - } -} - -// Ends current write and calls delegate. -- (void)completeCurrentWrite -{ - NSAssert (theCurrentWrite, @"Trying to complete current write when there is no current write."); - - if ([theDelegate respondsToSelector:@selector(onSocket:didWriteDataWithTag:)]) - { - [theDelegate onSocket:self didWriteDataWithTag:theCurrentWrite->tag]; - } - - if (theCurrentWrite != nil) [self endCurrentWrite]; // Caller may have disconnected. -} - -// Ends current write. -- (void)endCurrentWrite -{ - NSAssert (theCurrentWrite, @"Trying to complete current write when there is no current write."); - - [theWriteTimer invalidate]; - theWriteTimer = nil; - - [theCurrentWrite release]; - theCurrentWrite = nil; - - [self maybeScheduleDisconnect]; -} - -// Checks to see if all writes have been completed for disconnectAfterWriting. -- (void)maybeScheduleDisconnect -{ - if(theFlags & kDisconnectSoon) - { - if(([theWriteQueue count] == 0) && (theCurrentWrite == nil)) - { - [self performSelector:@selector(disconnect) withObject:nil afterDelay:0]; - } - } -} - -- (void)doWriteTimeout:(NSTimer *)timer -{ - if (timer != theWriteTimer) return; // Old timer. Ignore it. - if (theCurrentWrite != nil) - { - [self endCurrentWrite]; - } - [self closeWithError:[self getWriteTimeoutError]]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark CF Callbacks -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -- (void)doCFSocketCallback:(CFSocketCallBackType)type - forSocket:(CFSocketRef)sock - withAddress:(NSData *)address - withData:(const void *)pData -{ - NSParameterAssert ((sock == theSocket) || (sock == theSocket6)); - - switch (type) - { - case kCFSocketConnectCallBack: - // The data argument is either NULL or a pointer to an SInt32 error code, if the connect failed. - if(pData) - [self doSocketOpen:sock withCFSocketError:kCFSocketError]; - else - [self doSocketOpen:sock withCFSocketError:kCFSocketSuccess]; - break; - case kCFSocketAcceptCallBack: - [self doAcceptWithSocket: *((CFSocketNativeHandle *)pData)]; - break; - default: - NSLog (@"AsyncSocket %p received unexpected CFSocketCallBackType %d.", self, type); - break; - } -} - -- (void)doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream -{ - NSParameterAssert(theReadStream != NULL); - - CFStreamError err; - switch (type) - { - case kCFStreamEventOpenCompleted: - [self doStreamOpen]; - break; - case kCFStreamEventHasBytesAvailable: - [self doBytesAvailable]; - break; - case kCFStreamEventEndEncountered: - if([theDelegate respondsToSelector:@selector(onReadStreamEnded:)]){ - if([theDelegate onReadStreamEnded:self] == NO) break; - } - case kCFStreamEventErrorOccurred: - err = CFReadStreamGetError (theReadStream); - [self closeWithError: [self errorFromCFStreamError:err]]; - break; - default: - NSLog (@"AsyncSocket %p received unexpected CFReadStream callback, CFStreamEventType %d.", self, type); - } -} - -- (void)doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream -{ - NSParameterAssert(theWriteStream != NULL); - - CFStreamError err; - switch (type) - { - case kCFStreamEventOpenCompleted: - [self doStreamOpen]; - break; - case kCFStreamEventCanAcceptBytes: - [self doSendBytes]; - break; - case kCFStreamEventErrorOccurred: - case kCFStreamEventEndEncountered: - err = CFWriteStreamGetError (theWriteStream); - [self closeWithError: [self errorFromCFStreamError:err]]; - break; - default: - NSLog (@"AsyncSocket %p received unexpected CFWriteStream callback, CFStreamEventType %d.", self, type); - } -} - -/** - * This is the callback we setup for CFSocket. - * This method does nothing but forward the call to it's Objective-C counterpart - **/ -static void MyCFSocketCallback (CFSocketRef sref, CFSocketCallBackType type, CFDataRef address, const void *pData, void *pInfo) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - AsyncSocket *socket = [[(AsyncSocket *)pInfo retain] autorelease]; - [socket doCFSocketCallback:type forSocket:sref withAddress:(NSData *)address withData:pData]; - - [pool release]; -} - -/** - * This is the callback we setup for CFReadStream. - * This method does nothing but forward the call to it's Objective-C counterpart - **/ -static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - AsyncSocket *socket = [[(AsyncSocket *)pInfo retain] autorelease]; - [socket doCFReadStreamCallback:type forStream:stream]; - - [pool release]; -} - -/** - * This is the callback we setup for CFWriteStream. - * This method does nothing but forward the call to it's Objective-C counterpart - **/ -static void MyCFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - AsyncSocket *socket = [[(AsyncSocket *)pInfo retain] autorelease]; - [socket doCFWriteStreamCallback:type forStream:stream]; - - [pool release]; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Class Methods -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Return line separators. -+ (NSData *)CRLFData -{ - return [NSData dataWithBytes:"\x0D\x0A" length:2]; -} - -+ (NSData *)CRData -{ - return [NSData dataWithBytes:"\x0D" length:1]; -} - -+ (NSData *)LFData -{ - return [NSData dataWithBytes:"\x0A" length:1]; -} - -+ (NSData *)ZeroData -{ - return [NSData dataWithBytes:"" length:1]; -} - -@end diff --git a/engine/platform/ios/FtpConnection.h b/engine/platform/ios/FtpConnection.h deleted file mode 100644 index dc262166..00000000 --- a/engine/platform/ios/FtpConnection.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - iosFtpServer - Copyright (C) 2008 Richard Dearlove ( monsta ) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#import - -#import "AsyncSocket.h" - -#import "FtpDataConnection.h" -#import "FtpDefines.h" -#include -#include - - -@class FtpServer; - -@interface FtpConnection : NSObject { - AsyncSocket *connectionSocket; // Socket for this particular connection - FtpServer *server; // pointer to Server object - - AsyncSocket *dataListeningSocket; // Socket to listen for a data connection to spawn on <-- think this is now redundant - AsyncSocket *dataSocket; // duplicates the listening socket - remove listening socket from code when working - which seems to be the case. - - FtpDataConnection *dataConnection; // instance handling spawned data connection socket - - NSArray *msgComponents; // The Message rcvd broken into an array - UInt16 dataPort; - int transferMode; - NSMutableArray *queuedData; - - NSString *currentUser; // The current user for this connection - NSString *currentDir; // The current directory for this connection - NSString *currentFile; // File that is about to be uploaded - NSFileHandle *currentFileHandle; // File handle of what to save - - NSString *rnfrFilename; // rnfr - - -} - --(id)initWithAsyncSocket:(AsyncSocket*)newSocket forServer:(id)myServer ; -#pragma mark STATE - -@property(readwrite)int transferMode; - -@property(readwrite, retain ) NSString *currentFile; -@property(readwrite, retain ) NSString *currentDir; -@property(readwrite, retain ) NSString *rnfrFilename; - --(NSString*)connectionAddress; - - -#pragma mark ASYNCSOCKET DATACONN - --(BOOL)openDataSocket:(int)portNumber; --(int)choosePasvDataPort; - - --(BOOL)onSocketWillConnect:(AsyncSocket *)sock; --(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket; - -#pragma mark ASYNCSOCKET FTPCLIENT CONNECTION --(void)onSocket:(AsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag; --(void)onSocket:(AsyncSocket*)sock didWriteDataWithTag:(long)tag; --(void)sendMessage:(NSString*)ftpMessage; // calls FC writedata --(void)sendDataString:(NSString*)dataString; // calls FDC writedata --(void)sendData:(NSMutableData*)data; --(void)didReceiveDataWritten; // notification that FDC wrote data --(void)didReceiveDataRead; // notification that FDC read data ie a transfer --(void)didFinishReading; // Called at the closing == end of a data connection from the client we presume - -#pragma mark PROCESS - --(void)processDataRead:(NSData*)data; --(void)processCommand; - -#pragma mark COMMANDS --(void)doQuit:(id)sender arguments:(NSArray*)arguments; --(void)doUser:(id)sender arguments:(NSArray*)arguments; --(void)doPass:(id)sender arguments:(NSArray*)arguments; --(void)doStat:(id)sender arguments:(NSArray*)arguments; --(void)doFeat:(id)sender arguments:(NSArray*)arguments; --(void)doList:(id)sender arguments:(NSArray*)arguments; --(void)doPwd:(id)sender arguments:(NSArray*)arguments; --(void)doNoop:(id)sender arguments:(NSArray*)arguments; --(void)doSyst:(id)sender arguments:(NSArray*)arguments; --(void)doLprt:(id)sender arguments:(NSArray*)arguments; --(void)doPasv:(id)sender arguments:(NSArray*)arguments; --(void)doEpsv:(id)sender arguments:(NSArray*)arguments; --(void)doPort:(id)sender arguments:(NSArray*)arguments; --(void)doNlst:(id)sender arguments:(NSArray*)arguments; --(void)doStor:(id)sender arguments:(NSArray*)arguments; --(void)doRetr:(id)sender arguments:(NSArray*)arguments; --(void)doDele:(id)sender arguments:(NSArray*)arguments; --(void)doMlst:(id)sender arguments:(NSArray*)arguments; --(void)doSize:(id)sender arguments:(NSArray*)arguments; --(void)doMkdir:(id)sender arguments:(NSArray*)arguments; --(void)doCdUp:(id)sender arguments:(NSArray*)arguments; --(void)doRnfr:(id)sender arguments:(NSArray*)arguments; --(void)doRnto:(id)sender arguments:(NSArray*)arguments; - -#pragma mark UTITILITES --(NSString*)makeFilePathFrom:(NSString*)filename; --(unsigned long long)fileSize:(NSString*)filePath; --(NSString*)fileNameFromArgs:(NSArray*)arguments; -- (Boolean)changedCurrentDirectoryTo:(NSString *)newDirectory; --(Boolean)canChangeDirectoryTo:(NSString *)testDirectory; -- (Boolean)accessibleFilePath:(NSString*)filePath; // check filepath exists and is in basedir ( if set ) -- (Boolean)validNewFilePath:(NSString*)filePath; -- (NSString *)visibleCurrentDir; --(NSString *)rootedPath:(NSString*)path; - -@end diff --git a/engine/platform/ios/FtpConnection.m b/engine/platform/ios/FtpConnection.m deleted file mode 100644 index 5ab35d54..00000000 --- a/engine/platform/ios/FtpConnection.m +++ /dev/null @@ -1,1220 +0,0 @@ -/* - iosFtpServer - Copyright (C) 2008 Richard Dearlove ( monsta ) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#import "FtpConnection.h" -#import "FtpServer.h" - - -@implementation FtpConnection - -@synthesize transferMode; -@synthesize currentFile; -@synthesize currentDir; -@synthesize rnfrFilename; - -// ---------------------------------------------------------------------------------------------------------- --(id)initWithAsyncSocket:(AsyncSocket*)newSocket forServer:(id)myServer -// ---------------------------------------------------------------------------------------------------------- -{ - self = [super init ]; - if (self) - { - connectionSocket = [newSocket retain ]; - server = myServer; - [ connectionSocket setDelegate:self ]; - [ connectionSocket writeData:DATASTR(@"220 iosFtp server ready.\r\n") withTimeout:-1 tag:0 ]; // send out the welcome message to the client - [ connectionSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; // start listening for commands on this connection to client - dataListeningSocket = nil; - dataPort=2001; - transferMode = pasvftp; - queuedData = [[ NSMutableArray alloc ] init ]; // A buffer for sending data when the connection isn't quite up yet - self.currentDir = [ server.baseDir copy]; // the working directory for this connection, Starts in the directory the server is set to. set chroot=true in server code to sandbox in - currentFile = nil; - currentFileHandle = nil; // not saving to a file yet - rnfrFilename = nil; - - currentUser = nil; - NSLog(@"FC: Current Directory starting at %@",currentDir ); - } - return self; -} -// ---------------------------------------------------------------------------------------------------------- --(void)dealloc -// ---------------------------------------------------------------------------------------------------------- -{ - if(connectionSocket) - { - [connectionSocket setDelegate:nil]; - [connectionSocket disconnect]; - [connectionSocket release]; - } - - if(dataListeningSocket){ // 12/2/10 - think this code is now redundant, dataListeningSocket, does it do anything anymore...? - [dataListeningSocket setDelegate:nil]; - [dataListeningSocket disconnect]; - [dataListeningSocket release]; - - } - if(dataSocket) - { - [dataSocket setDelegate:nil]; - [dataSocket disconnect]; - [dataSocket release]; - - } - if(dataConnection)[dataConnection release]; - // [msgComponents release]; - if(queuedData)[queuedData release]; - if(currentFile)[currentFile release]; - if(currentUser)[currentUser release]; - [currentDir release]; - if(currentFileHandle) [currentFileHandle release]; - - [super dealloc]; -} -#pragma mark STATE -// ---------------------------------------------------------------------------------------------------------- -//-(void)setTransferMode:(int)mode -// ---------------------------------------------------------------------------------------------------------- -//{ -// transferMode = mode; -//} - -// ---------------------------------------------------------------------------------------------------------- --(NSString*)connectionAddress -// ---------------------------------------------------------------------------------------------------------- -{ - return [connectionSocket connectedHost]; - -} - - -// ========================================================================================================== -#pragma mark CHOOSE DATA SOCKET -// ========================================================================================================== - - - -// ---------------------------------------------------------------------------------------------------------- --(BOOL)openDataSocket:(int)portNumber -// ---------------------------------------------------------------------------------------------------------- -{ - NSString *responseString; - NSError *error = nil; - - if (dataSocket) // Code changed 2010-02-12 - As per Jon's fix. stops memory leak on socket and connection - { - [dataSocket release]; - dataSocket = nil; - } - dataSocket = [ [ AsyncSocket alloc ] initWithDelegate:self ]; // create our socket for Listening or Direct Connection - if (dataConnection) - { - [dataConnection release]; - dataConnection = nil; - } - - - - switch (transferMode) { - case portftp: - dataPort = portNumber; - responseString = [ NSString stringWithFormat:@"200 PORT command successful."]; - [ dataSocket connectToHost:[self connectionAddress] onPort:portNumber error:&error ]; - // sleep(1); - dataConnection = [[ FtpDataConnection alloc ] initWithAsyncSocket:dataSocket forConnection:self withQueuedData:queuedData ]; - - break; - - case lprtftp: // FIXME wrong return message - dataPort = portNumber; - responseString = [ NSString stringWithFormat:@"228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...)", dataPort >>8, dataPort & 0xff]; - [ dataSocket connectToHost:[self connectionAddress] onPort:portNumber error:&error ]; - dataConnection = [[ FtpDataConnection alloc ] initWithAsyncSocket:dataSocket forConnection:self withQueuedData:queuedData ]; - break; - - case eprtftp: - dataPort = portNumber; - responseString = @"200 EPRT command successful."; - [ dataSocket connectToHost:[self connectionAddress] onPort:portNumber error:&error ]; - dataConnection = [[ FtpDataConnection alloc ] initWithAsyncSocket:dataSocket forConnection:self withQueuedData:queuedData ]; - break; - - case pasvftp: - dataPort = [ self choosePasvDataPort ]; - NSString *address = [ [connectionSocket localHost ] stringByReplacingOccurrencesOfString:@"." withString:@"," ]; // autoreleased - responseString = [ NSString stringWithFormat:@"227 Entering Passive Mode (%@,%d,%d)",address, dataPort >>8, dataPort & 0xff]; - [ dataSocket acceptOnPort: dataPort error:&error ]; - dataConnection = nil; // will pickup from the listening socket - break; - - case epsvftp: - dataPort = [ self choosePasvDataPort ]; - responseString = [ NSString stringWithFormat:@"229 Entering Extended Passive Mode (|||%d|)", dataPort ]; - [ dataSocket acceptOnPort: dataPort error:&error ]; - - dataConnection = nil; // will pickup from the listening socket - break; - - - default: - break; - } - NSLog( @"-- %@", [ error localizedDescription ] ); - - [ self sendMessage:responseString ]; - - return YES; -} - - -// ---------------------------------------------------------------------------------------------------------- --(int)choosePasvDataPort -// ---------------------------------------------------------------------------------------------------------- -{ - struct timeval tv; - unsigned short int seed[3]; - - gettimeofday(&tv, NULL); - seed[0] = (tv.tv_sec >> 16) & 0xFFFF; - seed[1] = tv.tv_sec & 0xFFFF; - seed[2] = tv.tv_usec & 0xFFFF; - seed48(seed); - - int portNumber; - portNumber = (lrand48() % 64512) + 1024; - // NSLog(@"New Port number is %i", portNumber ); - - return portNumber; // FIXME - presetting to 2001 for the moment - - //return 2001; - -} - - - -// ========================================================================================================== -#pragma mark ASYNCSOCKET DATACONNECTION -// ========================================================================================================== - - -// ---------------------------------------------------------------------------------------------------------- --(BOOL)onSocketWillConnect:(AsyncSocket *)sock -// ---------------------------------------------------------------------------------------------------------- -{ - NSLog(@"FC:onSocketWillConnect"); - [ sock readDataWithTimeout:READ_TIMEOUT tag:0 ]; - - return YES; -} - -// ---------------------------------------------------------------------------------------------------------- --(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket // should be for a data connection on 2001 -// ---------------------------------------------------------------------------------------------------------- -{ - // opened socket for passive data - new socket connected to this which is the passive connection - - NSLog(@"FC:New Connection -- should be for the data port"); - - dataConnection = [[ FtpDataConnection alloc ] initWithAsyncSocket:newSocket forConnection:self withQueuedData:queuedData]; -} - - - -// ========================================================================================================== -#pragma mark ASYNCSOCKET FTPCLIENT CONNECTION -// ========================================================================================================== - -// ---------------------------------------------------------------------------------------------------------- --(void)onSocket:(AsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag // DATA READ -// ---------------------------------------------------------------------------------------------------------- -{ - - NSLog(@"FC:didReadData"); - [ connectionSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; // start reading again - - [ self processDataRead:data ]; - -} - - -// ---------------------------------------------------------------------------------------------------------- --(void)onSocket:(AsyncSocket*)sock didWriteDataWithTag:(long)tag // DATA WRITTEN -// ---------------------------------------------------------------------------------------------------------- -{ - [ connectionSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; // start reading again - // NSLog(@"FC:didWriteData"); - -} - -// ---------------------------------------------------------------------------------------------------------- --(void)sendMessage:(NSString*)ftpMessage // REDUNDANT really - FIXME -// ---------------------------------------------------------------------------------------------------------- -{ - NSLog(@">%@",ftpMessage ); - NSMutableData *dataString = [[ ftpMessage dataUsingEncoding:NSUTF8StringEncoding ] mutableCopy]; // Autoreleased - [ dataString appendData:[AsyncSocket CRLFData] ]; - - [ connectionSocket writeData:dataString withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; - [dataString release]; - [ connectionSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; // start reading again - // [ connectionSocket readDataWithTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; -} -// ---------------------------------------------------------------------------------------------------------- --(void)sendDataString:(NSString*)dataString -// ---------------------------------------------------------------------------------------------------------- -{ - NSMutableString *message = [[NSMutableString alloc] initWithString:dataString]; - CFStringNormalize((CFMutableStringRef)message, kCFStringNormalizationFormC); - NSMutableData *data = [[ message dataUsingEncoding:server.clientEncoding ] mutableCopy]; // Autoreleased - [message release]; - - if (dataConnection ) - { NSLog(@"FC:sendData"); - [ dataConnection writeData:data ]; - - } - else - { - [ queuedData addObject:data ]; - } - [data release]; - -} -// ---------------------------------------------------------------------------------------------------------- --(void)sendData:(NSMutableData*)data -// ---------------------------------------------------------------------------------------------------------- -{ - if (dataConnection ) - { NSLog(@"FC:sendData"); - [ dataConnection writeData:data ]; - } - else - { - [ queuedData addObject:data ]; - } - -} -// ---------------------------------------------------------------------------------------------------------- --(void)didReceiveDataWritten // notification from FtpDataConnection that the dataWasWritten -// ---------------------------------------------------------------------------------------------------------- -{ - - - - NSLog(@"SENDING COMPLETED"); - - [ self sendMessage:@"226 Transfer complete." ]; // send completed message to client - [ dataConnection closeConnection ]; -} - -// ---------------------------------------------------------------------------------------------------------- --(void)didReceiveDataRead // notification from FtpDataConnection that the dataWasWritten -// ---------------------------------------------------------------------------------------------------------- -{ - - NSLog(@"FC:didReceiveDataRead"); - - // must have sent a file - - // Should start writing file out if not written yet : FIXNOW - - if ( currentFileHandle != nil ) - { - // Append data on - NSLog(@"FC:Writing File to %@", currentFile ); - [ currentFileHandle writeData:dataConnection.receivedData ]; - - } - else - { - NSLog(@"Couldnt write data"); - } - - - -} - -// ---------------------------------------------------------------------------------------------------------- --(void)didFinishReading // Called at the end of a data connection from the client we presume -// ---------------------------------------------------------------------------------------------------------- -{ - if (currentFile) - { - - NSLog(@"Closing File Handle"); - [currentFile release]; - currentFile = nil; - } - else - { - NSLog(@"FC:Data Sent but not sure where its for "); - } - - [ self sendMessage:@"226 Transfer complete." ]; // send completed message to client - - // [ dataConnection closeConnection ]; // It must be closed, it dropped us - - if ( currentFileHandle != nil ) - { - NSLog(@"Closing File Handle"); - - [ currentFileHandle closeFile ]; // Close the file handle - [ currentFileHandle release ]; - currentFileHandle = nil; - [ server didReceiveFileListChanged]; - } - - dataConnection.connectionState = clientQuiet; - -} - - -// ========================================================================================================== -#pragma mark PROCESS -// ========================================================================================================= - -// ---------------------------------------------------------------------------------------------------------- --(void)processDataRead:(NSData*)data // convert to commands as Client Connection -// ---------------------------------------------------------------------------------------------------------- -{ - NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length] - 2)]; // remove last 2 chars - NSString *crlfmessage = [[[NSString alloc] initWithData:strData encoding:server.clientEncoding] autorelease]; - NSString *message; - - // message = [ crlfmessage stringByReplacingOccurrencesOfString:@"\r\n" withString:@""]; // gets autoreleased - - message = [ crlfmessage stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet ]]; - NSLog(@"<%@",message ); - msgComponents = [message componentsSeparatedByString:@" "]; // change this to use spaces - for the FTP protocol - - [ self processCommand ]; - - [connectionSocket readDataToData:[AsyncSocket CRLFData] withTimeout:-1 tag:0 ]; // force to readdata CHECK -} - -// ---------------------------------------------------------------------------------------------------------- --(void)processCommand // assumes data has been place in Array msgComponents -// ---------------------------------------------------------------------------------------------------------- -{ - NSString *commandString = [ msgComponents objectAtIndex:0]; - - if ([ commandString length ] > 0) // If there is a command here - { - // Search through dictionary for correct matching command and method that it calls - - NSString *commandSelector = [ [[server commands] objectForKey:[commandString lowercaseString] ] stringByAppendingString:@"arguments:"]; - - if ( commandSelector ) // If we have a matching command - { - - SEL action = NSSelectorFromString(commandSelector); // Turn into a method - - if ( [ self respondsToSelector:action ]) // If we respond to this method - { - // DO COMMAND - [self performSelector:action withObject:self withObject:msgComponents ]; // Preform method with arguments - } - else - { // UNKNOWN COMMAND - NSString *outputString =[ NSString stringWithFormat:@"500 '%@': command not understood.", commandString ]; - [ self sendMessage:outputString ]; - - NSLog(@"DONT UNDERSTAND"); - } - } - else // UNKNOWN COMMAND - { - NSString *outputString =[ NSString stringWithFormat:@"500 '%@': command not understood.", commandString ]; - [ self sendMessage:outputString ]; - } - } - else - { - // Write out an error msg - } - -} - -// ========================================================================================================== -#pragma mark COMMANDS -// ========================================================================================================== - -// ---------------------------------------------------------------------------------------------------------- --(void)doQuit:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - NSLog(@"Quit : %@",arguments); - - [ self sendMessage:@"221- Data traffic for this session was 0 bytes in 0 files"]; - - [self sendMessage:@"221 Thank you for using the FTP service on localhost." ]; - - if(connectionSocket) - { - [connectionSocket disconnectAfterWriting ]; // Will this close the socket ? - // [connectionSocket disconnect]; - } - - [ server closeConnection:self ]; // Tell the server to close us down, remove us from the list of connections - - - // FIXME - delete the dataconnection if its open - - -} -// ---------------------------------------------------------------------------------------------------------- --(void)doUser:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - // send out confirmation message -- 331 password required for - if ( currentUser != nil ) - [currentUser release]; - currentUser = [[ arguments objectAtIndex:1 ] retain]; - NSString *outputString = [ NSString stringWithFormat:@"331 Password required for %@", currentUser ]; - [ sender sendMessage:outputString]; -} -// ---------------------------------------------------------------------------------------------------------- --(void)doPass:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ -// NSString *pass = [ arguments objectAtIndex:1 ]; - NSString *outputString = [ NSString stringWithFormat:@"230 User %@ logged in.", currentUser ]; - [ sender sendMessage:outputString]; -} -// ---------------------------------------------------------------------------------------------------------- --(void)doStat:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - // Send out stat message - [ sender sendMessage:@"211-localhost FTP server status:"]; - // FIXME - add in the stats - [ sender sendMessage:@"211 End of Status"]; -} -// ---------------------------------------------------------------------------------------------------------- --(void)doFeat:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - [ sender sendMessage:@"211-Features supported"]; - - // If encoding is UTF8, notify the client - if (server.clientEncoding == NSUTF8StringEncoding) - [ sender sendMessage:@" UTF8" ]; - - [ sender sendMessage:@"211 End"]; -} - - - - -// ---------------------------------------------------------------------------------------------------------- --(void)doList:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - // Get the name of any additional directory that we are asked to list, if its empty we use the current directory - - NSString *lsDir = [ self fileNameFromArgs:arguments] ; // autoreleased, get the directory that we're being asked for - NSString *listText; - - - if ([lsDir length]<1) { - lsDir = currentDir; - } - else { - lsDir = [self rootedPath:lsDir ]; - } - - - - NSLog( @"doList currentDir(%@) changeRoot%d", lsDir, server.changeRoot ); - NSLog(@"Will list %@ ",lsDir); - listText = [ [ server createList:lsDir] retain ]; // Can list directory so do it -// if ([self canChangeDirectoryTo:lsDir]) { -// listText = [ [ server createList:lsDir] retain ]; // Can list directory so do it -// } -// else { -// listText = @""; // return nothing as not in chroot -// } - - NSLog( @"doList sending this. %@", listText ); - - [ sender sendMessage:@"150 Opening ASCII mode data connection for '/bin/ls'."]; - - [ sender sendDataString:listText ]; - [listText release ]; -} -// ---------------------------------------------------------------------------------------------------------- --(void)doPwd:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - - NSLog(@"Will PWD %@ ",[self visibleCurrentDir]); - - // CHECKME - changed to show basedir - currentdir - - - NSString *cmdString = [ NSString stringWithFormat:@"257 \"%@\" is the current directory.", [self visibleCurrentDir] ]; // autoreleased - [ sender sendMessage:cmdString ]; // FIXME - seems to be buggy on ftp command line client - -} -// ---------------------------------------------------------------------------------------------------------- --(void)doNoop:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - [sender sendMessage:@"200 NOOP command successful." ]; - -} -// ---------------------------------------------------------------------------------------------------------- --(void)doSyst:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - [ sender sendMessage:@"215 UNIX Type: L8 Version: iosFtp 20080912" ]; -} -// ---------------------------------------------------------------------------------------------------------- --(void)doLprt:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - // LPRT,"6,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,231,92" - NSString *socketDesc = [ arguments objectAtIndex:1 ] ; - NSArray *socketAddr = [ socketDesc componentsSeparatedByString:@"," ]; - - int hb = [[socketAddr objectAtIndex:19] intValue ]; - int lb = [[socketAddr objectAtIndex:20] intValue ]; - - NSLog(@"%d %d %d",hb <<8, hb,lb ); - int clientPort = (hb <<8 ) + lb; - - [sender setTransferMode:lprtftp]; - - [ sender openDataSocket:clientPort ]; -} -// ---------------------------------------------------------------------------------------------------------- --(void)doEprt:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - // EPRT |2|1080::8:800:200C:417A|5282| - - NSString *socketDesc = [ arguments objectAtIndex:1 ] ; - NSArray *socketAddr = [ socketDesc componentsSeparatedByString:@"|" ]; - - NSString *item; - for (item in socketAddr) { - NSLog(@"%@", item); - } - int clientPort = [[ socketAddr objectAtIndex:3 ] intValue ]; - - NSLog(@"Got Send Port %d", clientPort ); - - [sender setTransferMode:eprtftp]; - // [ sender initDataSocket:clientPort ]; - [ sender openDataSocket:clientPort ]; -} - - -// ---------------------------------------------------------------------------------------------------------- --(void)doPasv:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - [sender setTransferMode:pasvftp]; - // [ sender initDataSocket:0 ]; - [ sender openDataSocket:0 ]; - -} -// ---------------------------------------------------------------------------------------------------------- --(void)doEpsv:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - // FIXME - open a port random high address - [sender setTransferMode:epsvftp]; - // [ sender initDataSocket:0 ]; - [ sender openDataSocket:0 ]; -} -// ---------------------------------------------------------------------------------------------------------- --(void)doPort:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - int hb, lb; - - // PORT 127,0,0,1,197,251 looks like this - // get 2nd argument and split up by , and then take the last 2 bits - - NSString *socketDesc = [ arguments objectAtIndex:1 ] ; - NSArray *socketAddr = [ socketDesc componentsSeparatedByString:@"," ]; - - hb = [[socketAddr objectAtIndex:4] intValue ]; - lb = [[socketAddr objectAtIndex:5] intValue ]; - - - int clientPort = (hb <<8 ) + lb; - - - [sender setTransferMode:portftp]; - - [ sender openDataSocket:clientPort ]; - -} - -// ---------------------------------------------------------------------------------------------------------- --(void)doOpts:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - NSString *cmd = [ arguments objectAtIndex:1 ]; - NSString *cmdstr = [ NSString stringWithFormat:@"502 Unknown command '%@'",cmd ]; - [ sender sendMessage:cmdstr ]; - // 502 Unknown command 'sj' -} -// ---------------------------------------------------------------------------------------------------------- --(void)doType:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - // 200 Type set to A. - - // FIXME - change the data output to the matching type -- we dont do anything with this yet,, there are many types apart from this one. - - NSString *cmd = [ arguments objectAtIndex:1 ]; - - //if ( [ [cmd lowercaseString] isEqualToString:@"i" ]) - // { - NSString *cmdstr = [ NSString stringWithFormat:@"200 Type set to %@.",cmd ]; - [ sender sendMessage:cmdstr ]; - // } - // else - // { - // NSString *cmdstr = [ NSString stringWithFormat:@"500 'type %@': command not understood.",cmd ]; - // [ sender sendMessage:cmdstr ]; - // } -} -// ---------------------------------------------------------------------------------------------------------- --(void)doCwd:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - // 250 - // 500, 501, 502, 421, 530, 550 - // 250 CWD command successful. - - NSLog(@"DoCwd arguments is %@",arguments); - - NSString *cmdstr; - NSString *cwdDir = [ self fileNameFromArgs:arguments] ; // autoreleased, get the directory that we're being asked for - - if ( [ self changedCurrentDirectoryTo:cwdDir ] ) // tries to change to that directory, checks in bounds and viable - { - cmdstr = [ NSString stringWithFormat:@"250 OK. Current directory is %@", [self visibleCurrentDir]]; // currentDir is now in the new place - cmdstr = @"250 CWD command successful."; - } - else - { - cmdstr = @"550 CWD failed."; - } - - [ sender sendMessage:cmdstr]; - - NSLog(@"currentDir is now %@",currentDir ); - -} -// ---------------------------------------------------------------------------------------------------------- --(void)doNlst:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - [self doList:sender arguments:arguments ]; -} -// ---------------------------------------------------------------------------------------------------------- --(void)doStor:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - // eg STOR my filename here.mp3 STOR=0 my=1 filename=2 etc - - NSFileManager *fs = [NSFileManager defaultManager]; - NSString *filename = [ self fileNameFromArgs:arguments]; // autoreleased - NSString *cmdstr; - - self.currentFile = [self makeFilePathFrom:filename]; // makes a filepath, using absolute or relative filename - - // Check this falls within the area of filesystem we are allowed to write to - if ( [self validNewFilePath:self.currentFile] ) // FIXME - finish function for test - { - // CREATE and then OPEN NEW FILE FOR WRITING - if ([fs createFileAtPath:self.currentFile contents:nil attributes:nil]==YES) - { - currentFileHandle = [[ NSFileHandle fileHandleForWritingAtPath:currentFile] retain]; // Open the file handle to write to - cmdstr = [ NSString stringWithFormat:@"150 Opening BINARY mode data connection for '%@'.",filename ]; // autoreleased - } - else - { - // couldn't make file, send out the error - cmdstr = [ NSString stringWithFormat:@"553 %@: Permission denied.", filename ]; - - } - } - else - { - // couldn't make file as out of root area - cmdstr = [ NSString stringWithFormat:@"553 %@: Permission denied.", filename ]; - } - - NSLog(@"FC:doStor %@", currentFile ); - - - [sender sendMessage:cmdstr]; - - // CHECKME - data connection should have been brought up by client? - if (dataConnection ) // if not we're in trouble. mind u currentFile is doing similar job. - { - NSLog(@"FC:setting connection state to clientSending"); - dataConnection.connectionState = clientSending; - } - else - { - NSLog(@"FC:Erorr Cant set connection state to Client Sending : no Connection yet "); - } - - - -} - -// ---------------------------------------------------------------------------------------------------------- --(void)doRetr:(id)sender arguments:(NSArray*)arguments // DOWNLOAD to CLIENT -// ---------------------------------------------------------------------------------------------------------- -{ - BOOL isDir; - NSString *cmdstr; - - NSString *filename = [self fileNameFromArgs:arguments]; // autoreleased - NSString *filePath = [self makeFilePathFrom:filename]; // turns relative or absolute path into format we need - - NSLog(@"FC:doRetr: %@", filePath ); - - if ( [self accessibleFilePath:filePath ] ) // if this filepath is in rooted area, and is a real file - { - if ( [ [ NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory: &isDir ]) // FIXME - fold into previous line - { - if ( isDir ){ // reject if its a directory request - [ sender sendMessage: [NSString stringWithFormat:@"550 %@: Not a plain file.",filename]]; - } - else // SEND FILE - { // FIXME URGENT - need to stop loading whole file into memory to send - NSMutableData *fileData = [[ NSMutableData dataWithContentsOfFile:filePath ] retain]; // FIXME - open in bits ? seems risky opening file in one piece - cmdstr = [ NSString stringWithFormat:@"150 Opening BINARY mode data connection for '%@'.",filename ]; - [sender sendMessage:cmdstr]; - - [ sender sendData:fileData ]; // Send file - [fileData release ]; - } - } - } - else // doesn't exist or not in basedir sandbox - { - cmdstr = [ NSString stringWithFormat:@"50 %@ No such file or directory.",filename ]; - NSLog(@"FC:doRetr: file %@ doesnt' exist ", filePath); - [sender sendMessage:cmdstr]; - } - - -} - -// ---------------------------------------------------------------------------------------------------------- --(void)doDele:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - NSString *cmdStr; - NSError *error; - NSString *filename =[self fileNameFromArgs:arguments]; // autoreleased - NSString *filePath = [self makeFilePathFrom:filename]; - - NSLog(@"filename is %@",filename); - - // attempt to delete the file - - if ( [self accessibleFilePath:filePath ]) // exists and can access - { - if ([[ NSFileManager defaultManager ] removeItemAtPath:filePath error:&error ]) - { - cmdStr = [ NSString stringWithFormat:@"250 DELE command successful.",filename ]; - [ server didReceiveFileListChanged]; // Addition by Brendan copied in by Rich 16/12/08 - } - else - { - cmdStr = [ NSString stringWithFormat:@"550 DELE command unsuccessful.",filename ]; // FIXME put correct error code in - } - } - else - { - cmdStr = [ NSString stringWithFormat:@"550 %@ No such file or directory.", filename]; - } - - [ sender sendMessage:cmdStr ]; - - -} -// ---------------------------------------------------------------------------------------------------------- --(void)doMlst:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - NSString *filename = [self fileNameFromArgs: arguments]; - NSString *cmdstr = [ NSString stringWithFormat:@"150 Opening BINARY mode data connection for '%@'.",filename ]; - // tell connection to expect a file - - [sender sendMessage:cmdstr]; // FIXME - this doesn't do anything beyond respond with first message - /* typiccal output to generate - 250: MLST 2012.pdf - Type=file;Size=2420017;Modify=20080808074805;Perm=adfrw;Unique=AgAADpIHZwA; /Users/monsta/Documents/2012.pdf - End - */ -} -// ---------------------------------------------------------------------------------------------------------- --(void)doSize:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - NSString *cmdStr; - NSString *filename = [self fileNameFromArgs: arguments]; // Autoreleased - NSString *filePath = [self makeFilePathFrom:filename]; // Autoreleased - - if ([self accessibleFilePath:filePath]) // Can we reach that file ? - { - if ([self fileSize:filePath] < 10240) // If small enough for old style size command - { - cmdStr = [ NSString stringWithFormat:@"213 %qu",[self fileSize:filePath] ]; // report size - } - else - { - cmdStr = [ NSString stringWithFormat:@"550 %@ file too large for SIZE.",filename ]; - } - } - else - { - cmdStr = [ NSString stringWithFormat:@"550 %@ No such file or directory.",filename ]; // report file not found - } - - // tell connection to expect a file - - [sender sendMessage:cmdStr]; - -} - - -// ---------------------------------------------------------------------------------------------------------- --(void)doMkdir:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - NSLog(@"current dir is %@",currentDir); - - NSString *cmdStr; - NSString *p = [self makeFilePathFrom:[self fileNameFromArgs:arguments]]; - NSFileManager *fs = [NSFileManager defaultManager]; - - if ( [self validNewFilePath:p ]) // FIXME - make sure function works. see function - { - if( [fs fileExistsAtPath:p isDirectory:nil] ) - { - cmdStr = [ NSString stringWithFormat:@"Error %@ exists",[self fileNameFromArgs:arguments] ]; - } - else - { - // FIXME - check that its ok to make a directory in this position - - [fs createDirectoryAtPath:p withIntermediateDirectories:YES attributes:nil error:nil]; - cmdStr = [ NSString stringWithFormat:@"250 MKD command successful."]; - - } - } - [sender sendMessage:cmdStr]; -} - -// ---------------------------------------------------------------------------------------------------------- --(void)doCdUp:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - NSLog(@"CurrentDir is %@",[self visibleCurrentDir]); - - NSString *upDir=[[self visibleCurrentDir] stringByDeletingLastPathComponent]; - - - - if ( [self changedCurrentDirectoryTo:upDir] ) // checks to see if its ok before moving - { - [sender sendMessage:@"250 CDUP command successful."]; - } - else - { - // create message saying you cant go to that directory - FIXME - [ sender sendMessage:@"550 CDUP command failed." ]; // CHECKME - look at a typical ftp mkdr command failure message - } -} - -// ---------------------------------------------------------------------------------------------------------- --(void)doRnfr:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - self.rnfrFilename = [self makeFilePathFrom:[self fileNameFromArgs:arguments]]; - - if ( [ self accessibleFilePath:self.rnfrFilename ] ) // FIXME - finish function - { - - if ( [[ NSFileManager defaultManager] fileExistsAtPath: rnfrFilename ] ) - { - [ sender sendMessage:@"350 RNFR command successful." ]; - } - else - [ sender sendMessage:@"550 RNFR command failed." ]; - } -} - -// ---------------------------------------------------------------------------------------------------------- --(void)doRnto:(id)sender arguments:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - // FIXME - check its ok to use the new filename - ie in sandbox/basedir - - if ( self.rnfrFilename == nil ){ - [ sender sendMessage:@"550 RNTO command failed." ]; - return; - } - - NSError *error; - NSString *rntoFilename = [self makeFilePathFrom:[self fileNameFromArgs:arguments]]; - - NSLog( @"%@", rnfrFilename ); - NSLog( @"%@", rntoFilename ); - - if ([self validNewFilePath:rntoFilename]) // FIXME - finish function - { - if ( [[ NSFileManager defaultManager] moveItemAtPath:rnfrFilename toPath: rntoFilename error:&error ] ){ - [ server didReceiveFileListChanged]; - [ sender sendMessage:@"250 RNTO command successful." ]; - } - else{ - NSString *errorString = [error localizedDescription]; - NSLog( @"RNTO failed %@", errorString ); - [ sender sendMessage:@"550 RNTO command failed." ]; - } -// [rnfrFilename release]; -// rnfrFilename = nil; -// [rntoFilename release]; // auto released, should be fine to just forget - - } - else - { - [ sender sendMessage:@"550 RNTO command failed." ]; // CHECKME - have a look at a typical server response for this error - } -} - -///////////////////////////////////////// -#pragma mark UTILITIES - -// ---------------------------------------------------------------------------------------------------------- --(NSString*)makeFilePathFrom:(NSString*)filename -// ---------------------------------------------------------------------------------------------------------- -{ - if ( [filename characterAtIndex:0] == '/' ) // if absolute file position - { - if ( server.changeRoot ) // then its actually relative to basedir - { - return [ [server.baseDir stringByAppendingPathComponent: filename] stringByResolvingSymlinksInPath ]; - } - else - { - return [[filename copy] autorelease ]; // Don't want to hang onto a variable, user should retain if needed. - } - - } - else // add onto the current file postion - { - return [ currentDir stringByAppendingPathComponent:filename]; // FIXME / CHECKME - need to set autorelease or retain etc. - - } -} - -// ---------------------------------------------------------------------------------------------------------- --(unsigned long long)fileSize:(NSString*)filePath -// ---------------------------------------------------------------------------------------------------------- -{ - - - NSError *error; - NSDictionary *fileAttribs = [ [ NSFileManager defaultManager ] attributesOfItemAtPath:filePath error:&error ]; - - NSNumber *fileSize = [ fileAttribs valueForKey:NSFileSize ]; - NSLog(@"File size is %qu ", [fileSize unsignedLongLongValue]); - return [ fileSize unsignedLongLongValue]; - -} -// ---------------------------------------------------------------------------------------------------------- --(NSString*)fileNameFromArgs:(NSArray*)arguments -// ---------------------------------------------------------------------------------------------------------- -{ - NSString *filename = [NSString string]; - NSLog(@"%@", [arguments description ] ); - if ([arguments count] >1) { - - - for ( int n=1; n<[arguments count]; n++) { // Start at arg2, - // test to see if argument begins with a - as it might be just a parameter, not the name - if (![[[arguments objectAtIndex:n] substringToIndex:1] isEqualToString:@"-"]) - { - if ([filename length]==0) { - filename = [arguments objectAtIndex:n ]; - } - else { - filename = [ NSString stringWithFormat:@"%@ %@", filename, [arguments objectAtIndex:n ] ]; // autoreleased - } - - - } - else { - NSLog(@"HYPHEN FOUND IGNORE"); - } - - - } - } - - - - return filename ; // autoreleased -} - - -// ---------------------------------------------------------------------------------------------------------- -- (Boolean)changedCurrentDirectoryTo:(NSString *)newDirectory // NOTE - there is probably a unix way of expanding pathnames and taking all the ../. stuff out. : should look this up -// ---------------------------------------------------------------------------------------------------------- -{ - NSFileManager *fileManager = [ NSFileManager defaultManager ]; - - NSString *currentDirectory = [ fileManager currentDirectoryPath ]; // store current directory - NSString *testDirectory, *expandedPath; - - - expandedPath = [[self rootedPath:newDirectory ] retain ]; - - // TEST IF ALLOWED TO USE THAT DIRECTORY ( ie is it in allowable filesystem ) - - if (![self canChangeDirectoryTo:expandedPath]) { - return false; - } - - - // CHANGE TO NEW DIRECTORY & GET SYSTEM FILE PATH ( no .. etc ) - if ( ! [ fileManager changeCurrentDirectoryPath:expandedPath ] ) // try changing to new directory - { - return false; // Not a valid directory - } - - testDirectory = [ fileManager currentDirectoryPath ]; // get new directory as string - - - // CHANGE BACK - if ( ! [ fileManager changeCurrentDirectoryPath:currentDirectory ] )// Change back to our own directory - { - return false; // Not a valid directory, shouldnt happen, but could u know - } - - - - self.currentDir = [ testDirectory copy ]; // make a copy, and retain release - - [ expandedPath release]; - return true; // its fine. would have failed before if not possible -} -// ---------------------------------------------------------------------------------------------------------- --(Boolean)canChangeDirectoryTo:(NSString *)testDirectory // NOTE - there is probably a unix way of expanding pathnames and taking all the ../. stuff out. : should look this up -// ---------------------------------------------------------------------------------------------------------- -{ - if ( [server changeRoot] && ( ! [ testDirectory hasPrefix:server.baseDir ]) ) { - return false; - } - else { - return true; - } - - -} -// ---------------------------------------------------------------------------------------------------------- -- (Boolean)accessibleFilePath:(NSString*)filePath // checks its a proper name, and also in the chroot sandbox if set -// ---------------------------------------------------------------------------------------------------------- -{ - // check, file is accessible within servers rights - // && file exists - - // FIXME - need to expand the filename reference somehow to check where it really is, whether in sandbox. praps look at tnftpd src code for this. - - return [ [ NSFileManager defaultManager ] fileExistsAtPath:filePath ]; -} -// ---------------------------------------------------------------------------------------------------------- -- (Boolean)validNewFilePath:(NSString*)filePath -// ---------------------------------------------------------------------------------------------------------- -{ - return true; // FIXME - check this is within the area we can write to -} -// ---------------------------------------------------------------------------------------------------------- -- (NSString *)visibleCurrentDir -// ---------------------------------------------------------------------------------------------------------- -{ - - if ( server.changeRoot ) // if root changed, to basedir - { - int alength = [server.baseDir length ]; // length of basedir - - NSLog(@"Length is %u", alength ); - NSString *aString = [ currentDir substringFromIndex:alength ]; // get the bit after basedir - - if ( ! [ aString hasSuffix:@"/" ] ) // add a / if needed - { - aString = [ aString stringByAppendingString:@"/" ]; - } - - - // return with basedir prefix removed. - - return aString; // returns just the end part, not the base part - } - else - { - return currentDir; // return complete path - } - - -} -// ---------------------------------------------------------------------------------------------------------- --(NSString *)rootedPath:(NSString*)path -// ---------------------------------------------------------------------------------------------------------- -{ - NSString *expandedPath; - - // GET FULL PATH OF NEW DIRECTORY - if ( [ path characterAtIndex:0 ] == '/') // if its an absolute path - { - if ( server.changeRoot ) // if rooted, its a relative path really, so add to basedir - { - expandedPath = [ [server.baseDir stringByAppendingPathComponent: path] stringByResolvingSymlinksInPath ]; - } - else // it really is an absolute path - { - expandedPath = path; // use the absolute path - } - } - else // or append onto currentdir ( the one the client chose ), as its a relative path - { - expandedPath =[[ currentDir stringByAppendingPathComponent:path ] stringByResolvingSymlinksInPath]; - } - - expandedPath = [ expandedPath stringByStandardizingPath ]; - - return expandedPath; // Autoreleased etc - -} -@end diff --git a/engine/platform/ios/FtpDataConnection.h b/engine/platform/ios/FtpDataConnection.h deleted file mode 100644 index 057ab84c..00000000 --- a/engine/platform/ios/FtpDataConnection.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - iosFtpServer - Copyright (C) 2008 Richard Dearlove ( monsta ) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#import -#import "AsyncSocket.h" -#import "FtpDefines.h" - -@class FtpConnection; - -@interface FtpDataConnection : NSObject { - AsyncSocket *dataSocket; - FtpConnection *ftpConnection; // connection which generated data socket we are tied to - - AsyncSocket *dataListeningSocket; - id dataConnection; - NSMutableData *receivedData; - int connectionState; - -} --(id)initWithAsyncSocket:(AsyncSocket*)newSocket forConnection:(id)aConnection withQueuedData:(NSMutableArray*)queuedData; --(void)writeString:(NSString*)dataString; --(void)writeData:(NSMutableData*)data; --(void)writeQueuedData:(NSMutableArray*)queuedData; --(void)closeConnection; - -#pragma mark ASYNCSOCKET DELEGATES --(BOOL)onSocketWillConnect:(AsyncSocket *)sock; --(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket; --(void)onSocket:(AsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag; --(void)onSocket:(AsyncSocket*)sock didWriteDataWithTag:(long)tag; - --(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err; - -@property (readonly) NSMutableData *receivedData; -@property (readwrite) int connectionState; - - -@end diff --git a/engine/platform/ios/FtpDataConnection.m b/engine/platform/ios/FtpDataConnection.m deleted file mode 100644 index f80fb047..00000000 --- a/engine/platform/ios/FtpDataConnection.m +++ /dev/null @@ -1,192 +0,0 @@ -/* - iosFtpServer - Copyright (C) 2008 Richard Dearlove ( monsta ) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#import "FtpDataConnection.h" -#import "FtpConnection.h" - - -@implementation FtpDataConnection - -@synthesize receivedData; -@synthesize connectionState; - -// ---------------------------------------------------------------------------------------------------------- --(id)initWithAsyncSocket:(AsyncSocket*)newSocket forConnection:(id)aConnection withQueuedData:(NSMutableArray*)queuedData -// ---------------------------------------------------------------------------------------------------------- -{ - self = [super init ]; - if (self) - { - dataSocket = [newSocket retain ]; // Hang onto the socket that was generated - the FDC is retained by FC - ftpConnection = aConnection; - - [ dataSocket setDelegate:self ]; - - if ( [queuedData count ] ) - { - NSLog(@"FC:Write Queued Data"); - [self writeQueuedData:queuedData ]; - [ queuedData removeAllObjects ]; // Clear out queue - } - // [ dataSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; - [ dataSocket readDataWithTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; - dataListeningSocket = nil; - receivedData = nil; // [[ NSMutableData alloc ] init ] 12/nov/08 - no need for this. rcd is just a pointer - - connectionState = clientQuiet; // Nothing coming through - } - return self; -} -// ---------------------------------------------------------------------------------------------------------- --(void)dealloc -// ---------------------------------------------------------------------------------------------------------- -{ - - [dataSocket release]; - [dataListeningSocket release]; - [dataConnection release]; -// [receivedData release]; - - - [super dealloc]; -} -// ---------------------------------------------------------------------------------------------------------- --(void)writeString:(NSString*)dataString -// ---------------------------------------------------------------------------------------------------------- -{ - NSLog(@"FDC:writeStringData"); - - NSMutableData *data = [[ dataString dataUsingEncoding:NSUTF8StringEncoding ] mutableCopy]; // Autoreleased - [ data appendData:[AsyncSocket CRLFData] ]; - - [ dataSocket writeData:data withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; - [ dataSocket readDataWithTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; -} - -// ---------------------------------------------------------------------------------------------------------- --(void)writeData:(NSMutableData*)data -// ---------------------------------------------------------------------------------------------------------- -{ - NSLog(@"FDC:writeData"); -// [ data appendData:[AsyncSocket CRLFData] ]; // Add on CRLF to end of data - as Windows Explorer needs it - - connectionState = clientReceiving; // We hope - - [ dataSocket writeData:data withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; - - [ dataSocket readDataWithTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; -} -// ---------------------------------------------------------------------------------------------------------- --(void)writeQueuedData:(NSMutableArray*)queuedData -// ---------------------------------------------------------------------------------------------------------- -{ - for (NSMutableData* data in queuedData) { - [self writeData:data ]; - } -} - -// ---------------------------------------------------------------------------------------------------------- --(void)closeConnection -// ---------------------------------------------------------------------------------------------------------- -{ - NSLog(@"FDC:closeConnection"); - [ dataSocket disconnect ]; - -} -#pragma mark ASYNCSOCKET DELEGATES -// ---------------------------------------------------------------------------------------------------------- --(BOOL)onSocketWillConnect:(AsyncSocket *)sock -// ---------------------------------------------------------------------------------------------------------- -{ - - NSLog(@"FDC:onSocketWillConnect"); - [ dataSocket readDataWithTimeout:READ_TIMEOUT tag:0 ]; - return YES; -} - -// ---------------------------------------------------------------------------------------------------------- --(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket -// ---------------------------------------------------------------------------------------------------------- -{ - // This shouldnt happen - we should be connected already - and havent set up a listening socket - NSLog(@"FDC:New Connection -- shouldn't be called"); - -} - - - -// ---------------------------------------------------------------------------------------------------------- --(void)onSocket:(AsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag -// ---------------------------------------------------------------------------------------------------------- -{ - NSLog(@"FDC:didReadData"); - -// [ dataSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; // continue reading - [ dataSocket readDataWithTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; - - NSMutableData *mutdata = [data mutableCopy]; - receivedData = [ mutdata retain]; // make autoreleased copy of data - - // notify connection data came through, ( will write data for us ) - [ftpConnection didReceiveDataRead ]; // notify, the connection, so it knows to write the data - [ receivedData release ]; // let go, not our business anymore - connectionState = clientSent; -} - - -// ---------------------------------------------------------------------------------------------------------- --(void)onSocket:(AsyncSocket*)sock didWriteDataWithTag:(long)tag -// ---------------------------------------------------------------------------------------------------------- -{ - NSLog(@"FDC:didWriteData"); - [ ftpConnection didReceiveDataWritten ]; // notify that we are finished writing - -// [ dataSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; // continue reading - [ dataSocket readDataWithTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; -} - - -// ---------------------------------------------------------------------------------------------------------- --(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err -// ---------------------------------------------------------------------------------------------------------- -{ - NSLog(@"FDC:willDisconnect"); - // if we were writing and there's no error, then it must be the end of file - - if ( connectionState == clientSending ) - { - NSLog(@"FDC::did FinishReading"); - // hopefully this is the end of the connection. not sure how we can tell - } - else - { - NSLog(@"FDC: we werent expecting this as we never set clientSending prob late coming up"); - } - [ ftpConnection didFinishReading ]; // its over, please send the message -} - -- (BOOL)onReadStreamEnded:(AsyncSocket*)sock -{ - NSLog( @"FDC: onReadStreamEnded %d(clientSending is %d)", connectionState, clientSending ); - if ( connectionState == clientSent || - connectionState == clientSending ) return YES; - return NO; -} - -@end diff --git a/engine/platform/ios/FtpDefines.h b/engine/platform/ios/FtpDefines.h deleted file mode 100644 index 5830ac25..00000000 --- a/engine/platform/ios/FtpDefines.h +++ /dev/null @@ -1,15 +0,0 @@ -enum { - pasvftp=0,epsvftp,portftp,lprtftp, eprtftp -}; - -#define DATASTR(args) [ args dataUsingEncoding:NSUTF8StringEncoding ] - -#define SERVER_PORT 20000 -#define READ_TIMEOUT -1 - -#define FTP_CLIENT_REQUEST 0 - -enum { - - clientSending=0, clientReceiving=1, clientQuiet=2,clientSent=3 -}; diff --git a/engine/platform/ios/FtpServer.h b/engine/platform/ios/FtpServer.h deleted file mode 100644 index f736c3d7..00000000 --- a/engine/platform/ios/FtpServer.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - iosFtpServer - Copyright (C) 2008 Richard Dearlove ( monsta ) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#import - - -#import "AsyncSocket.h" -#import "FtpDefines.h" -#import "FtpConnection.h" -#import "list.h" - -@interface FtpServer : NSObject { - - AsyncSocket *listenSocket; - NSMutableArray *connectedSockets; - id server; - id notificationObject; - - int portNumber; - id delegate; - - NSMutableArray *connections; - - NSDictionary *commands; - NSString *baseDir; - Boolean changeRoot; // Change root to virtual root ( basedir ) - int clientEncoding; // FTP client encoding type -} -- (id)initWithPort:(unsigned)serverPort withDir:(NSString *)aDirectory notifyObject:(id)sender; -- (void)stopFtpServer; - -#pragma mark ASYNCSOCKET DELEGATES -- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket; -- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port; - -#pragma mark NOTIFICATIONS -- (void)didReceiveFileListChanged; - -#pragma mark CONNECTIONS -- (void)closeConnection:(id)theConnection; -- (NSString *)createList:(NSString *)directoryPath; - -@property (readwrite, retain) AsyncSocket *listenSocket; -@property (readwrite, retain) NSMutableArray *connectedSockets; -@property (readwrite, retain) id server; -@property (readwrite, retain) id notificationObject; -@property (readwrite) int portNumber; -@property (readwrite, retain) id delegate; -@property (readwrite, retain) NSMutableArray *connections; -@property (readwrite, retain) NSDictionary *commands; -@property (readwrite, retain) NSString *baseDir; -@property (readwrite) Boolean changeRoot; -@property (readwrite) int clientEncoding; - -@end diff --git a/engine/platform/ios/FtpServer.m b/engine/platform/ios/FtpServer.m deleted file mode 100644 index 5f801561..00000000 --- a/engine/platform/ios/FtpServer.m +++ /dev/null @@ -1,198 +0,0 @@ -/* - iosFtpServer - Copyright (C) 2008 Richard Dearlove ( monsta ) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -// 11/02/08 changes to made to allow stopping of the server. Also added a bit of code to show users IP address. -// Cleaned up the code to make the retains and releases a bit more obvious... mostly programming style preferences. -// Added a stop Ftp server method to be called from where ever it was start. See iphoneLibTestViewController.m to see how I was calling it. -// Most users wouldn't want the server to continue to run once the transfers are done. - - -#import "FtpServer.h" - -@implementation FtpServer - -@synthesize listenSocket, connectedSockets, server, notificationObject, portNumber, delegate, commands, baseDir, connections; - -@synthesize clientEncoding; -@synthesize changeRoot; - -// ---------------------------------------------------------------------------------------------------------- -- (id)initWithPort:(unsigned)serverPort withDir:(NSString*)aDirectory notifyObject:(id)sender -// ---------------------------------------------------------------------------------------------------------- -{ - if( self = [super init] ) { - - NSError *error = nil; - - self.notificationObject = sender; - - // Load up commands - NSString *plistPath = [[ NSBundle mainBundle ] pathForResource:@"ftp_commands" ofType:@"plist"]; - if ( ! [ [ NSFileManager defaultManager ] fileExistsAtPath:plistPath ] ) - { - NSLog(@"ftp_commands.plist missing"); - } - commands = [ [ NSDictionary alloc ] initWithContentsOfFile:plistPath]; - - // Clear out connections list - NSMutableArray *myConnections = [[NSMutableArray alloc] init]; - self.connections = myConnections; - [myConnections release]; - - - - // Create a socket - self.portNumber = serverPort; - - AsyncSocket *myListenSocket = [[AsyncSocket alloc] initWithDelegate:self]; - self.listenSocket = myListenSocket; - [myListenSocket release]; - - NSLog(@"Listening on %u", portNumber); - [listenSocket acceptOnPort:serverPort error:&error]; // start lisetning on this port. - - NSMutableArray *myConnectedSockets = [[NSMutableArray alloc] initWithCapacity:1]; - self.connectedSockets = myConnectedSockets; - [myConnectedSockets release]; - - // Set directory - have to do this because on iphone, some directories arent what they report back as, so need real path it resolves to, CHECKME - might be an easier way - NSFileManager *fileManager = [ NSFileManager defaultManager ]; - NSString *expandedPath = [ aDirectory stringByStandardizingPath ]; - - // CHANGE TO NEW DIRECTORY & GET SYSTEM FILE PATH ( no .. etc ) - if ([ fileManager changeCurrentDirectoryPath:expandedPath ]) // try changing to directory - { - - self.baseDir = [[ fileManager currentDirectoryPath ] copy] ; // Gets the real path. CHECKME. - // self.baseDir = @"/Users"; // REMOVEME - added for testing 7/6/10 - } - else - { - self.baseDir = aDirectory; // shouldnt get to this line really - } - - self.changeRoot = false; // true if you want them to be sandboxed/chrooted into the basedir - - // the default client encoding is UTF8 - self.clientEncoding = NSUTF8StringEncoding; - } - return self; -} -// ---------------------------------------------------------------------------------------------------------- --(void)stopFtpServer -// ---------------------------------------------------------------------------------------------------------- -{ - if(listenSocket)[listenSocket disconnect]; - [connectedSockets removeAllObjects]; - - [connections removeAllObjects]; - -} -#pragma mark ASYNCSOCKET DELEGATES -// ---------------------------------------------------------------------------------------------------------- -- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket -// ---------------------------------------------------------------------------------------------------------- -{ - - FtpConnection *newConnection = [[[ FtpConnection alloc ] initWithAsyncSocket:newSocket forServer:self] autorelease]; // Create an ftp connection - - - - - [ connections addObject:newConnection ]; // Add this to our list of connections - - NSLog(@"FS:didAcceptNewSocket port:%i", [sock localPort]); - - if ([sock localPort] == portNumber ) - { - NSLog(@"Connection on Server Port"); - - } - else - { - // must be a data comms port - // spawn a data comms port - // look for the connection with the same port - // and attach it - NSLog(@"--ERROR %i, %i", [sock localPort],portNumber); - - } -} - -// ---------------------------------------------------------------------------------------------------------- -- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port -// ---------------------------------------------------------------------------------------------------------- -{ - NSLog(@"FtpServer:didConnectToHost port:%i", [sock localPort]); -} - -#pragma mark NOTIFICATIONS -// ---------------------------------------------------------------------------------------------------------- --(void)didReceiveFileListChanged -// ---------------------------------------------------------------------------------------------------------- -{ - if ([notificationObject respondsToSelector:@selector(didReceiveFileListChanged)]) - [notificationObject didReceiveFileListChanged ]; -} -#pragma mark CONNECTIONS -// ---------------------------------------------------------------------------------------------------------- -- (void)closeConnection:(id)theConnection -// ---------------------------------------------------------------------------------------------------------- -{ - // Search through connections for this one - and delete - // this should release it - and delloc - - [connections removeObject:theConnection ]; - - -} - -// ---------------------------------------------------------------------------------------------------------- --(NSString*)createList:(NSString*)directoryPath -// ---------------------------------------------------------------------------------------------------------- -{ - return createList(directoryPath); - - -} - -// ---------------------------------------------------------------------------------------------------------- -- (void)dealloc -// ---------------------------------------------------------------------------------------------------------- -{ - - - if(listenSocket) - { - [listenSocket disconnect]; - [listenSocket release]; - } - - [connectedSockets release]; - [notificationObject release]; - [connections release]; - [commands release]; - [baseDir release]; - [super dealloc]; - -} - - -@end diff --git a/engine/platform/ios/NetworkController.h b/engine/platform/ios/NetworkController.h deleted file mode 100644 index 5723a419..00000000 --- a/engine/platform/ios/NetworkController.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// networkController.h -// DiddyDJ -// -// Created by Richard Dearlove on 21/10/2008. -// Copyright 2008 DiddySoft. All rights reserved. -// - -#import -#import -#include -#include -#include - #include -#include - -@interface NetworkController : NSObject { - -} -+ (NSString *)localWifiIPAddress; -+ (NSString *) localIPAddress; -+ (BOOL)addressFromString:(NSString *)IPAddress address:(struct sockaddr_in *)address; -+ (NSString *) getIPAddressForHost: (NSString *) theHost; -+ (BOOL) hostAvailable: (NSString *) theHost; -+ (BOOL) connectedToNetwork; -@end diff --git a/engine/platform/ios/NetworkController.m b/engine/platform/ios/NetworkController.m deleted file mode 100644 index 55893a84..00000000 --- a/engine/platform/ios/NetworkController.m +++ /dev/null @@ -1,184 +0,0 @@ -// -// networkController.m -// -// Created by Richard Dearlove on 21/10/2008. -// Copyright 2008 DiddySoft. All rights reserved. -// - -#import "NetworkController.h" - -@implementation NetworkController - -// Return the localized IP address - -// ---------------------------------------------------------------------------------------------------------- -+ (NSString *)localWifiIPAddress -// ---------------------------------------------------------------------------------------------------------- - -{ - NSString *address = @"error"; - struct ifaddrs *interfaces = NULL; - struct ifaddrs *temp_addr = NULL; - int success = 0; - - // retrieve the current interfaces - returns 0 on success - success = getifaddrs(&interfaces); - if (success == 0) - { - // Loop through linked list of interfaces - temp_addr = interfaces; - while(temp_addr != NULL) - { - if(temp_addr->ifa_addr->sa_family == AF_INET) - { - // Check if interface is en0 which is the wifi connection on the iPhone - if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) - { - // Get NSString from C String - address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]; - } - } - - temp_addr = temp_addr->ifa_next; - } - } - - // Free memory - freeifaddrs(interfaces); - - return address; -} -// ---------------------------------------------------------------------------------------------------------- - -// ---------------------------------------------------------------------------------------------------------- -+ (NSString *) localIPAddress -// ---------------------------------------------------------------------------------------------------------- -{ - char baseHostName[255]; - gethostname(baseHostName, 255); - - // Adjust for iPhone -- add .local to the host name - char hn[255]; - sprintf(hn, "%s.local", baseHostName); - - struct hostent *host = gethostbyname(hn); - if (host == NULL) - { - herror("resolv"); - return NULL; - } - else { - struct in_addr **list = (struct in_addr **)host->h_addr_list; - return [NSString stringWithCString:inet_ntoa(*list[0])]; - } - - return NULL; -} - -// ---------------------------------------------------------------------------------------------------------- -+ (BOOL)addressFromString:(NSString *)IPAddress address:(struct sockaddr_in *)address -// ---------------------------------------------------------------------------------------------------------- -{ - if (!IPAddress || ![IPAddress length]) { - return NO; - } - - memset((char *) address, sizeof(struct sockaddr_in), 0); - address->sin_family = AF_INET; - address->sin_len = sizeof(struct sockaddr_in); - - int conversionResult = inet_aton([IPAddress UTF8String], &address->sin_addr); - if (conversionResult == 0) { - NSAssert1(conversionResult != 1, @"Failed to convert the IP address string into a sockaddr_in: %@", IPAddress); - return NO; - } - - return YES; -} - -// ---------------------------------------------------------------------------------------------------------- -+ (NSString *) getIPAddressForHost: (NSString *) theHost -// ---------------------------------------------------------------------------------------------------------- -{ - struct hostent *host = gethostbyname([theHost UTF8String]); - - if (host == NULL) { - herror("resolv"); - return NULL; - } - - struct in_addr **list = (struct in_addr **)host->h_addr_list; - NSString *addressString = [NSString stringWithCString:inet_ntoa(*list[0])]; - return addressString; -} - - -// ---------------------------------------------------------------------------------------------------------- -+ (BOOL) hostAvailable: (NSString *) theHost -// ---------------------------------------------------------------------------------------------------------- -{ - - NSString *addressString = [self getIPAddressForHost:theHost]; - if (!addressString) - { - printf("Error recovering IP address from host name\n"); - return NO; - } - - struct sockaddr_in address; - BOOL gotAddress = [self addressFromString:addressString address:&address]; - - if (!gotAddress) - { - printf("Error recovering sockaddr address from %s\n", [addressString UTF8String]); - return NO; - } - - SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&address); - SCNetworkReachabilityFlags flags; - - BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags); - CFRelease(defaultRouteReachability); - - if (!didRetrieveFlags) - { - printf("Error. Could not recover network reachability flags\n"); - return NO; - } - - BOOL isReachable = flags & kSCNetworkFlagsReachable; - return isReachable ? YES : NO;; -} - - -// ---------------------------------------------------------------------------------------------------------- -+ (BOOL) connectedToNetwork -// ---------------------------------------------------------------------------------------------------------- -{ - // Create zero addy - struct sockaddr_in zeroAddress; - bzero(&zeroAddress, sizeof(zeroAddress)); - zeroAddress.sin_len = sizeof(zeroAddress); - zeroAddress.sin_family = AF_INET; - - // Recover reachability flags - SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress); - CFRelease(defaultRouteReachability); - - SCNetworkReachabilityFlags flags; - - BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags); - if (!didRetrieveFlags) - { - printf("Error. Could not recover network reachability flags\n"); - return 0; - } - - BOOL isReachable = flags & kSCNetworkFlagsReachable; - BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired; - // BOOL isEDGE = flags & kSCNetworkReachabilityFlagsIsWWAN; - return (isReachable && !needsConnection) ? YES : NO; -} - - -@end diff --git a/engine/platform/ios/bundle/ftp_commands.plist b/engine/platform/ios/bundle/ftp_commands.plist deleted file mode 100644 index a951c6be..00000000 --- a/engine/platform/ios/bundle/ftp_commands.plist +++ /dev/null @@ -1,64 +0,0 @@ - - - - - ack - doAck: - cdup - doCdUp: - cwd - doCwd: - dele - doDele: - eprt - doEprt: - epsv - doEpsv: - feat - doFeat: - list - doList: - lprt - doLprt: - mkd - doMkdir: - mlst - doMlst: - nlst - doList: - noop - doNoop: - opts - doOpts: - pass - doPass: - pasv - doPasv: - port - doPort: - pwd - doPwd: - quit - doQuit: - retr - doRetr: - rmd - doDele: - rnfr - doRnfr: - rnto - doRnto: - size - doSize: - stat - doStat: - stor - doStor: - syst - doSyst: - type - doType: - user - doUser: - - diff --git a/engine/platform/ios/launchdialog.m b/engine/platform/ios/launchdialog.m index 9c552ba6..9731312d 100644 --- a/engine/platform/ios/launchdialog.m +++ b/engine/platform/ios/launchdialog.m @@ -17,7 +17,6 @@ #import #import #import -#import "FtpServer.h" #include #include "dlfcn.h" @@ -93,35 +92,6 @@ const char *IOS_GetExecDir(void) return dir; } -UIBackgroundTaskIdentifier task; -FtpServer *server = NULL; - -void IOS_StartBackgroundTask(void) -{ - if( !server ) return; - - if( task != UIBackgroundTaskInvalid ) - return; - - UIApplication* app = [UIApplication sharedApplication]; - - task = [app beginBackgroundTaskWithExpirationHandler:^{ - [app endBackgroundTask:task]; - task = UIBackgroundTaskInvalid; - }]; - - // Start the long-running task and return immediately. - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - UIBackgroundTaskIdentifier local = task; - - // do not keep "zombie" tasks - while( 1 ) - sleep(1); - - [app endBackgroundTask:local]; - task = UIBackgroundTaskInvalid; - }); -} #define SETTINGS_MAGIC 111 @@ -203,8 +173,6 @@ void IOS_LaunchDialog( void ) UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 300, 200)]; - UISwitch *ftpswitch = [[UISwitch alloc] initWithFrame:CGRectMake(210,60,80,30)]; - UILabel *argstitle = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 30)]; [argstitle setText:@"Command-line arguments:"]; @@ -212,12 +180,6 @@ void IOS_LaunchDialog( void ) [args setBackgroundColor:[[UIColor alloc] initWithRed:1 green:1 blue:1 alpha:1]]; if(isdark) [args setBackgroundColor:[[UIColor alloc] initWithRed:0 green:0 blue:0 alpha:1]]; - - UILabel *ftptitle = [[UILabel alloc] initWithFrame:CGRectMake(0, 60, 200, 30)]; - [ftptitle setText:@"FTP Server"]; - - - UITextField *port = [[UITextField alloc] initWithFrame:CGRectMake(110, 60, 100, 30)]; UITextField *suffix = [[UITextField alloc] initWithFrame:CGRectMake(140, 90, 160, 30 )]; [suffix setBackgroundColor:[[UIColor alloc] initWithRed:1 green:1 blue:1 alpha:1]]; @@ -229,9 +191,6 @@ void IOS_LaunchDialog( void ) [scroll addSubview:argstitle]; [scroll addSubview:args]; [scroll addSubview:suffix]; - [scroll addSubview:ftpswitch]; - [scroll addSubview:ftptitle]; - [scroll addSubview:port]; [scroll addSubview:suffixtitle]; settingsfile = fopen( settingspath, "rb" ); @@ -240,14 +199,11 @@ void IOS_LaunchDialog( void ) settings.args[1023] = 0; settings.suffix[31] = 0; [args setText:@(settings.args)]; - [port setText:[NSString stringWithFormat:@"%u", settings.port]]; [suffix setText:@(settings.suffix)]; - ftpswitch.on = settings.ftpserver; } else { [args setText:@"-dev 2 -log"]; - [port setText:@"21135"]; } scroll.contentSize=CGSizeMake(250, 200); @@ -263,10 +219,8 @@ void IOS_LaunchDialog( void ) if( (settingsfile = fopen( settingspath, "wb" )) ) { - settings.ftpserver = ftpswitch.on; strlcpy(settings.args, [args.text UTF8String], 1024); strlcpy(settings.suffix, [suffix.text UTF8String], 32 ); - settings.port = [port.text intValue]; settings.magic = 111; fwrite(&settings, sizeof(settings), 1, settingsfile); @@ -278,25 +232,6 @@ void IOS_LaunchDialog( void ) exit(0); } - if( ftpswitch.on ) - { - - button = -1; - - [[[UIAlertView alloc] initWithTitle:@"Xash3D" message:[NSString stringWithFormat:@"Started FTP server on port %@", port.text] delegate:delegate cancelButtonTitle:@"Ok" otherButtonTitles:nil] show]; - - server = [[ FtpServer alloc ] initWithPort:[port.text integerValue] withDir:@(docsDir) notifyObject:nil ]; - - IOS_StartBackgroundTask(); - - @autoreleasepool { - while( button == -1 ) { - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; - IOS_StartBackgroundTask(); // keep running - } - } - } - NSArray *argv = [ args.text componentsSeparatedByString:@" " ]; int count = [argv count]; @@ -314,11 +249,8 @@ void IOS_LaunchDialog( void ) alert.delegate = nil; - [ftpswitch release]; - [ftptitle release]; [args release]; [argstitle release]; - [port release]; [suffix release]; [suffixtitle release]; diff --git a/engine/platform/ios/list.h b/engine/platform/ios/list.h deleted file mode 100644 index c4c7ac3e..00000000 --- a/engine/platform/ios/list.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - iosFtpServer - Copyright (C) 2008 Richard Dearlove ( monsta ) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#import - -#pragma mark LS replacement -NSString* createList(NSString* directoryPath); - -#pragma mark Supporting Functions -int filesinDirectory(NSString* filePath ); -NSMutableString* int2BinString(int x); -NSMutableString *byte2String(int x ); -NSMutableString *bin2perms(NSString *binaryValue); diff --git a/engine/platform/ios/list.m b/engine/platform/ios/list.m deleted file mode 100644 index 875819b6..00000000 --- a/engine/platform/ios/list.m +++ /dev/null @@ -1,202 +0,0 @@ -/* - iosFtpServer - Copyright (C) 2008 Richard Dearlove ( monsta ) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#import "list.h" - -// ---------------------------------------------------------------------------------------------------------- -NSString* createList(NSString* directoryPath) -// ---------------------------------------------------------------------------------------------------------- -{ - NSFileManager *fileManager = [ NSFileManager defaultManager ]; - NSDictionary *fileAttributes; - NSError *error; - - NSString* fileType; - NSNumber* filePermissions; - long fileSubdirCount; - NSString* fileOwner; - NSString* fileGroup; - NSNumber* fileSize; - NSDate* fileModified; - NSString* fileDateFormatted; - NSDateFormatter* dateFormatter = [[[ NSDateFormatter alloc ] init ] autorelease ]; - - BOOL fileIsDirectory; - - - NSMutableString* returnString= [ NSMutableString new ]; - NSString* formattedString; - - NSString* binaryString; - - [returnString appendString:@"\r\n"]; - - NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:directoryPath]; - NSString *filePath; - - NSString* firstChar; - NSString* fullFilePath; - - [dateFormatter setDateFormat:@"MMM dd HH:mm"]; - NSLocale *englishLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en"]; - [dateFormatter setLocale:englishLocale]; - [englishLocale release]; - - - NSLog(@"Get LS for %@", directoryPath ); - int numberOfFiles = 0; - while (filePath = [dirEnum nextObject]) { - - firstChar = [ filePath substringToIndex:1 ]; - - [dirEnum skipDescendents ]; // don't go down that recursive road - - if ( ![ firstChar isEqualToString:@"."] ) // dont show hidden files - { - fullFilePath = [directoryPath stringByAppendingPathComponent:filePath]; - - fileAttributes = [ fileManager attributesOfItemAtPath:fullFilePath error:&error ]; - - fileType = [ fileAttributes valueForKey:NSFileType ]; - - filePermissions = [ fileAttributes valueForKey:NSFilePosixPermissions ]; - fileSubdirCount = filesinDirectory(fullFilePath); - fileOwner = [ fileAttributes valueForKey:NSFileOwnerAccountName ]; - fileGroup = [ fileAttributes valueForKey:NSFileGroupOwnerAccountName ]; - fileSize = [ fileAttributes valueForKey:NSFileSize ]; - fileModified = [ fileAttributes valueForKey:NSFileModificationDate ]; - fileDateFormatted = [ dateFormatter stringFromDate:fileModified ]; - - fileIsDirectory = (fileType == NSFileTypeDirectory ); - - - fileSubdirCount = fileSubdirCount <1 ? 1 : fileSubdirCount; - - binaryString = int2BinString([filePermissions unsignedLongValue]) ; - binaryString = [ binaryString substringFromIndex:7 ];// snip off the front - formattedString = [ NSString stringWithFormat:@"%@%@ %5i %12@ %12@ %10qu %@ %@", fileIsDirectory ? @"d" : @"-" ,bin2perms(binaryString),fileSubdirCount, fileOwner, fileGroup, [fileSize unsignedLongLongValue], fileDateFormatted , filePath ]; - - [ returnString appendString:formattedString ]; - [ returnString appendString:@"\n" ]; - numberOfFiles++; - } - } - [returnString insertString: [NSString stringWithFormat:@"total %d", numberOfFiles] atIndex:0]; - // NSLog(returnString ); - return [returnString autorelease]; // FIXME - release count - -} -// ---------------------------------------------------------------------------------------------------------- -int filesinDirectory(NSString* filePath ) -// ---------------------------------------------------------------------------------------------------------- -{ - int no_files =0; - NSFileManager *fileManager = [ NSFileManager defaultManager ]; - NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:filePath]; - - while (filePath = [dirEnum nextObject]) { - [dirEnum skipDescendents ]; // don't want children - no_files++; - } - - return no_files; -} -// ---------------------------------------------------------------------------------------------------------- -NSMutableString* int2BinString(int x) -// ---------------------------------------------------------------------------------------------------------- -{ - NSMutableString *returnString = [[ NSMutableString alloc ] init]; - int hi, lo; - hi=(x>>8) & 0xff; - lo=x&0xff; - - [ returnString appendString:byte2String(hi) ]; - [ returnString appendString:byte2String(lo) ]; - return [ returnString autorelease ]; -} - - - -// ---------------------------------------------------------------------------------------------------------- -NSMutableString *byte2String(int x ) -// ---------------------------------------------------------------------------------------------------------- -{ - NSMutableString *returnString = [[ NSMutableString alloc ]init]; - - int n; - - for(n=0; n<8; n++) - { - if((x & 0x80) !=0) - { - - [ returnString appendString:@"1"]; - - - } - else - { - [ returnString appendString:@"0"]; - } - x = x<<1; - } - - return [returnString autorelease]; -} - -// ---------------------------------------------------------------------------------------------------------- -NSMutableString *bin2perms(NSString *binaryValue) -// ---------------------------------------------------------------------------------------------------------- -{ - NSMutableString *returnString = [[ NSMutableString alloc ] init]; - NSRange subStringRange; - subStringRange.length = 1; - NSString *replaceWithChar; - - for (int n=0; n < [binaryValue length]; n++) - { - subStringRange.location = n; - // take the char - // if pos = 0, 3,6 - if ( n == 0 || n == 3 || n ==6) - { - replaceWithChar = @"r"; - } - if ( n == 1 || n == 4 || n ==7) - { - replaceWithChar = @"w"; - } - if ( n == 2 || n == 5 || n ==7) - { - replaceWithChar = @"x"; - } - - if ( [[binaryValue substringWithRange:subStringRange ] isEqualToString:@"1" ] ) - { - [ returnString appendString:replaceWithChar ]; - } - else - { - [ returnString appendString:@"-" ]; - } - - } - - return [ returnString autorelease ]; -} - diff --git a/engine/platform/sdl2/host_sdl2.c b/engine/platform/sdl2/host_sdl2.c index 842dd323..ee9a45d9 100644 --- a/engine/platform/sdl2/host_sdl2.c +++ b/engine/platform/sdl2/host_sdl2.c @@ -248,13 +248,6 @@ static void SDLash_ActiveEvent( int gain ) } else { -#if TARGET_OS_IPHONE - { - // Keep running if ftp server enabled - void IOS_StartBackgroundTask( void ); - IOS_StartBackgroundTask(); - } -#endif host.status = HOST_NOFOCUS; if( cls.key_dest == key_game ) diff --git a/scripts/ios/createipa.sh b/scripts/ios/createipa.sh index a4453903..e1a85e25 100755 --- a/scripts/ios/createipa.sh +++ b/scripts/ios/createipa.sh @@ -12,7 +12,6 @@ if [ -d "$BUILDDIR" ]; then cp -r "$BUILDDIR/ios/libs/"* "$BUILDDIR/ios/xash3d.app" cp Info.plist "$BUILDDIR/ios/xash3d.app" - cp ftp_commands.plist "$BUILDDIR/ios/xash3d.app" if [ ! -d "$BUILDDIR/SDL2.framework" ]; then echo "Couldn't find SDL2.framework, place it in the build directory" exit 1