#if 0
#pragma mark Includes
#endif
#include <CFNetwork/CFNetwork.h>
#include "CFNetworkInternal.h" // for _CFNetConnectionCacheKey and friends
#include <CoreFoundation/CFStreamPriv.h>
#include "CFNetConnection.h"
#include <CFNetwork/CFFTPStream.h>
#include <CFNetwork/CFFTPStreamPriv.h>
#include <CoreFoundation/CFPriv.h>
#include <CFNetwork/CFSocketStreamPriv.h>
#include <CoreFoundation/CFPriv.h>
#include <CFNetwork/CFHTTPConnectionPriv.h> // for the asynchronous proxy lookup
#include "CFNetworkSchedule.h"
#include <SystemConfiguration/SystemConfiguration.h>
#if 0
#pragma mark *Win32 Specifics
#endif
#if defined(__WIN32__)
#include <sys/param.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#define SOCK_MAXADDRLEN 255
#undef ENOTCONN
#define ENOTCONN WSAENOTCONN
#undef EADDRNOTAVAIL
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
#define DT_UNKNOWN 0
#define DT_FIFO 1
#define DT_CHR 2
#define DT_DIR 4
#define DT_BLK 6
#define DT_REG 8
#define DT_LNK 10
#define DT_SOCK 12
#define DT_WHT 14
#else
#if 0
#pragma mark *Mach Specifics
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/dirent.h>
#include <netinet/tcp.h>
#endif
#if 0
#pragma mark -
#pragma mark Constants
#endif
const SInt32 kCFStreamErrorDomainFTP = 6;
#if 0
#pragma mark -
#pragma mark Constant Strings
#pragma mark *Stream Property Keys
#endif
CONST_STRING_DECL(kCFStreamPropertyFTPUserName, "kCFStreamPropertyFTPUserName")
CONST_STRING_DECL(kCFStreamPropertyFTPPassword, "kCFStreamPropertyFTPPassword")
CONST_STRING_DECL(kCFStreamPropertyFTPProxy, "kCFStreamPropertyFTPProxy")
CONST_STRING_DECL(kCFStreamPropertyFTPAttemptPersistentConnection, "kCFStreamPropertyFTPAttemptPersistentConnection")
CONST_STRING_DECL(kCFStreamPropertyFTPUsePassiveMode, "kCFStreamPropertyFTPUsePassiveMode")
CONST_STRING_DECL(kCFStreamPropertyFTPFetchResourceInfo, "kCFStreamPropertyFTPFetchResourceInfo")
CONST_STRING_DECL(kCFStreamPropertyFTPFileTransferOffset, "kCFStreamPropertyFTPFileTransferOffset")
CONST_STRING_DECL(_kCFStreamPropertyFTPLogInOnly, "_kCFStreamPropertyFTPLogInOnly") CONST_STRING_DECL(_kCFStreamPropertyFTPRemoveResource, "_kCFStreamPropertyFTPRemoveResource") CONST_STRING_DECL(_kCFStreamPropertyFTPNewResourceName, "_kCFStreamPropertyFTPNewResourceName") #ifdef __CONSTANT_CFSTRINGS__
#define kCFStreamPropertyFTPFetchNameList CFSTR("kCFStreamPropertyFTPFetchNameList")
#else
static CONST_STRING_DECL(kCFStreamPropertyFTPFetchNameList, "kCFStreamPropertyFTPFetchNameList") #endif
CONST_STRING_DECL(kCFStreamPropertyFTPResourceSize, "kCFStreamPropertyFTPResourceSize")
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFStreamPropertyFTPLastHTTPResponse CFSTR("_kCFStreamPropertyFTPLastHTTPResponse")
#else
static CONST_STRING_DECL(_kCFStreamPropertyFTPLastHTTPResponse, "_kCFStreamPropertyFTPLastHTTPResponse")
#endif
#if 0
#pragma mark *Various Dictionary Keys
#endif
CONST_STRING_DECL(kCFStreamPropertyFTPProxyHost, "FTPProxy")
CONST_STRING_DECL(kCFStreamPropertyFTPProxyPort, "FTPPort")
CONST_STRING_DECL(kCFStreamPropertyFTPProxyPassword, "kCFStreamPropertyFTPProxyPassword")
CONST_STRING_DECL(kCFStreamPropertyFTPProxyUser, "kCFStreamPropertyFTPProxyUser")
#define kResourceInfoItemCount 8L
CONST_STRING_DECL(kCFFTPResourceMode, "kCFFTPResourceMode")
CONST_STRING_DECL(kCFFTPResourceName, "kCFFTPResourceName")
CONST_STRING_DECL(kCFFTPResourceOwner, "kCFFTPResourceOwner")
CONST_STRING_DECL(kCFFTPResourceGroup, "kCFFTPResourceGroup")
CONST_STRING_DECL(kCFFTPResourceLink, "kCFFTPResourceLink")
CONST_STRING_DECL(kCFFTPResourceSize, "kCFFTPResourceSize")
CONST_STRING_DECL(kCFFTPResourceType, "kCFFTPResourceType")
CONST_STRING_DECL(kCFFTPResourceModDate, "kCFFTPResourceModDate")
#if 0
#pragma mark *Other Strings
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kFTPSchemeString CFSTR("ftp")
#define kFTPSSchemeString CFSTR("ftps")
#define kSOCKS4SchemeString CFSTR("socks4")
#define kSOCKS5SchemeString CFSTR("socsk5")
#define kHTTPSchemeString CFSTR("http")
#define kHTTPSSchemeString CFSTR("https")
#else
static CONST_STRING_DECL(kFTPSchemeString, "ftp")
static CONST_STRING_DECL(kFTPSSchemeString, "ftps")
static CONST_STRING_DECL(kSOCKS4SchemeString, "socks4")
static CONST_STRING_DECL(kSOCKS5SchemeString, "socsk5")
static CONST_STRING_DECL(kHTTPSchemeString, "http")
static CONST_STRING_DECL(kHTTPSSchemeString, "https")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kAnonymousUserString CFSTR("anonymous")
#define kAnonymousPasswordString CFSTR("cfnetwork@apple.com")
#else
static CONST_STRING_DECL(kAnonymousUserString, "anonymous")
static CONST_STRING_DECL(kAnonymousPasswordString, "cfnetwork@apple.com")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kFTPProxyFormat CFSTR("%@@%@")
#define kFTPProxyWithPortFormat CFSTR("%@@%@:%ld")
#else
static CONST_STRING_DECL(kFTPProxyFormat, "%@@%@")
static CONST_STRING_DECL(kFTPProxyWithPortFormat, "%@@%@:%ld")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kHTTPGETMethod CFSTR("GET")
#else
static CONST_STRING_DECL(kHTTPGETMethod, "GET")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kHTMLTagOpen CFSTR("<")
#define kHTMLTagClose CFSTR(">")
#else
static CONST_STRING_DECL(kHTMLTagOpen, "<")
static CONST_STRING_DECL(kHTMLTagClose, ">")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kCFFTPUSERCommandString CFSTR("USER %@\r\n")
#define kCFFTPPASSCommandString CFSTR("PASS %@\r\n")
#define kCFFTPSYSTCommandString CFSTR("SYST\r\n")
#define kCFFTPSITEDIRSTYLECommandString CFSTR("SITE DIRSTYLE\r\n")
#define kCFFTPSITETRUTHCommandString CFSTR("SITE TRUTH ON\r\n")
#define kCFFTPPWDCommandString CFSTR("PWD\r\n")
#define kCFFTPTYPECommandString CFSTR("TYPE I\r\n")
#define kCFFTPPASVCommandString CFSTR("PASV\r\n")
#define kCFFTPEPSVCommandString CFSTR("EPSV\r\n")
#define kCFFTPPORTCommandString CFSTR("PORT %lu,%lu,%lu,%lu,%lu,%lu\r\n")
#define kCFFTPEPRTCommandString CFSTR("EPRT |2|%x:%x:%x:%x:%x:%x:%x:%x|%lu|\r\n")
#define kCFFTPRESTCommandString CFSTR("REST %lld\r\n")
#define kCFFTPSTATCommandString CFSTR("STAT %@\r\n")
#define kCFFTPSIZECommandString CFSTR("SIZE %@\r\n")
#define kCFFTPRETRCommandString CFSTR("RETR %@\r\n")
#define kCFFTPNLSTCommandString CFSTR("NLST %@\r\n")
#define kCFFTPCWDCommandString CFSTR("CWD %@\r\n")
#define kCFFTPLISTCommandString CFSTR("LIST\r\n")
#define kCFFTPSTORCommandString CFSTR("STOR %@\r\n")
#define kCFFTPMKDCommandString CFSTR("MKD %@\r\n")
#define kCFFTPRMDCommandString CFSTR("RMD %@\r\n")
#define kCFFTPDELECommandString CFSTR("DELE %@\r\n")
#define kCFFTPRNFRCommandString CFSTR("RNFR %@\r\n")
#define kCFFTPRNTOCommandString CFSTR("RNTO %@\r\n")
#else
static CONST_STRING_DECL(kCFFTPUSERCommandString, "USER %@\r\n")
static CONST_STRING_DECL(kCFFTPPASSCommandString, "PASS %@\r\n")
static CONST_STRING_DECL(kCFFTPSYSTCommandString, "SYST\r\n")
static CONST_STRING_DECL(kCFFTPSITEDIRSTYLECommandString, "SITE DIRSTYLE\r\n")
static CONST_STRING_DECL(kCFFTPSITETRUTHCommandString, "SITE TRUTH ON\r\n")
static CONST_STRING_DECL(kCFFTPPWDCommandString, "PWD\r\n")
static CONST_STRING_DECL(kCFFTPTYPECommandString, "TYPE I\r\n")
static CONST_STRING_DECL(kCFFTPPASVCommandString, "PASV\r\n")
static CONST_STRING_DECL(kCFFTPEPSVCommandString, "EPSV\r\n")
static CONST_STRING_DECL(kCFFTPPORTCommandString, "PORT %lu,%lu,%lu,%lu,%lu,%lu\r\n")
static CONST_STRING_DECL(kCFFTPEPRTCommandString, "EPRT |2|%x:%x:%x:%x:%x:%x:%x:%x|%lu|\r\n")
static CONST_STRING_DECL(kCFFTPRESTCommandString, "REST %lld\r\n")
static CONST_STRING_DECL(kCFFTPSTATCommandString, "STAT %@\r\n")
static CONST_STRING_DECL(kCFFTPSIZECommandString, "SIZE %@\r\n")
static CONST_STRING_DECL(kCFFTPRETRCommandString, "RETR %@\r\n")
static CONST_STRING_DECL(kCFFTPNLSTCommandString, "NLST %@\r\n")
static CONST_STRING_DECL(kCFFTPCWDCommandString, "CWD %@\r\n")
static CONST_STRING_DECL(kCFFTPLISTCommandString, "LIST\r\n")
static CONST_STRING_DECL(kCFFTPSTORCommandString, "STOR %@\r\n")
static CONST_STRING_DECL(kCFFTPMKDCommandString, "MKD %@\r\n")
static CONST_STRING_DECL(kCFFTPRMDCommandString, "RMD %@\r\n")
static CONST_STRING_DECL(kCFFTPDELECommandString, "DELE %@\r\n")
static CONST_STRING_DECL(kCFFTPRNFRCommandString, "RNFR %@\r\n")
static CONST_STRING_DECL(kCFFTPRNTOCommandString, "RNTO %@\r\n")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kCFFTPPathFormatString CFSTR("%@%@")
#else
static CONST_STRING_DECL(kCFFTPPathFormatString, "%@%@")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kCFFTPRootPathString CFSTR("/")
#else
static CONST_STRING_DECL(kCFFTPRootPathString, "/")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kCFFTPForcedRootPathPrefix CFSTR("//")
#else
static CONST_STRING_DECL(kCFFTPForcedRootPathPrefix, "//");
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kCFFTPWindowsNTSystemString CFSTR("Windows_NT")
#define kCFFTPMSDOSSystemString CFSTR("MSDOS-like directory output is on")
#else
static CONST_STRING_DECL(kCFFTPWindowsNTSystemString, "Windows_NT")
static CONST_STRING_DECL(kCFFTPMSDOSSystemString, "MSDOS-like directory output is on")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kCFFTPOSXSystemString CFSTR("Mac OS X Server")
#else
static CONST_STRING_DECL(kCFFTPOSXSystemString, "Mac OS X Server")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kCFFTPStreamOpenCompleted CFSTR("_FTPStreamOpenCompleted")
#else
static CONST_STRING_DECL(kCFFTPStreamOpenCompleted, "_FTPStreamOpenCompleted")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kCFFTPStreamDescriptionFormat CFSTR("<FTPStream %p>{%@, url = %@, flags = 0x%x }")
#define kCFFTPStreamUploadDescription CFSTR("upload")
#define kCFFTPStreamDownloadDescription CFSTR("download")
#else
#error crap
static CONST_STRING_DECL(kCFFTPStreamDescriptionFormat, "<FTPStream %p>{%@, url = %@, flags = 0x%x }")
static CONST_STRING_DECL(kCFFTPStreamUploadDescription, "upload")
static CONST_STRING_DECL(kCFFTPStreamDownloadDescription, "download")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kCFFTPStreamEmptyString CFSTR("")
#else
static CONST_STRING_DECL(kCFFTPStreamEmptyString, "")
#endif
#if 0
#pragma mark -
#pragma mark Enum Values
#endif
typedef enum {
kFTPStateConnect = 0,
kFTPStateUSER,
kFTPStatePASS,
kFTPStateSYST,
kFTPStateSITEDIRSTYLE,
kFTPStateSITETRUTH,
kFTPStatePWD,
kFTPStateTYPE,
kFTPStateIdle,
kFTPStateCWD,
kFTPStatePASV,
kFTPStatePORT,
kFTPStateSTAT,
kFTPStateSIZE,
kFTPStateREST,
kFTPStateRETR, kFTPStateNLST,
kFTPStateLIST,
kFTPStateSTOR,
kFTPStateMKD,
kFTPStateRMD,
kFTPStateDELE,
kFTPStateRNFR,
kFTPStateRNTO
} _CFFTPStreamState;
enum {
kFlagBitPerformPASV = 0, kFlagBitDidSetPassiveBit, kFlagBitPerformSTAT, kFlagBitPerformNLST, kFlagBitIsHTTPRequest, kFlagBitReadHTTPResponse, kFlagBit407TriedOnce, kFlagBitPerformUpload, kFlagBitRemoveResource, kFlagBitLogInOnly, kFlagBitGotError,
kFlagBitMultiline = 0, kFlagBitReturnToIdle, kFlagBitIsXServer, kFlagBitLeftForDead, kFlagBitHTTPLitmus,
kBufferGrowthSize = 2048, kFTPTimeoutInSeconds = 180,
kCFNetConnectionTypeFTP,
kCFNetConnectionTypeFTPS,
kCFNetConnectionTypeFTPProxy,
kCFNetConnectionTypeFTPSProxy
};
#if 0
#pragma mark -
#pragma mark CFStream Context
#endif
typedef struct {
UInt32 _flags;
CFURLRef _url;
CFURLRef _newUrl;
CFTypeRef _dataStream;
CFTypeRef _userStream;
CFStreamError _error;
CFSocketRef _server;
CFDictionaryRef _attributes;
long long _offset;
CFMutableArrayRef _runloops;
CFMutableDictionaryRef _properties;
CFReadStreamRef _proxyStream;
CFArrayRef _proxies;
CFIndex _current;
_CFNetConnectionRef _connection;
} _CFFTPStreamContext;
#if 0
#pragma mark -
#pragma mark CFNetConnection Context
#endif
typedef struct {
UInt32 _flags;
_CFNetConnectionCacheKey _key;
UInt32 _result;
_CFFTPStreamState _state;
CFStringRef _root;
CFIndex _recvCount; CFIndex _sendCount;
CFMutableDataRef _recvBuffer; CFMutableDataRef _sendBuffer;
} _CFFTPNetConnectionContext;
#if 0
#pragma mark -
#pragma mark Static Function Declarations
#endif
static void _FTPStreamFinalize(CFTypeRef stream, _CFFTPStreamContext* ctxt);
static CFStringRef _FTPStreamCopyDescription(CFTypeRef stream, _CFFTPStreamContext* ctxt);
static Boolean _FTPStreamOpen(CFTypeRef stream, CFStreamError* error, Boolean* openComplete, _CFFTPStreamContext* ctxt);
static Boolean _FTPStreamOpenCompleted(CFTypeRef stream, CFStreamError* error, _CFFTPStreamContext* ctxt);
static CFIndex _FTPStreamRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, _CFFTPStreamContext* ctxt);
static Boolean _FTPStreamCanRead(CFReadStreamRef stream, _CFFTPStreamContext* ctxt);
static CFIndex _FTPStreamWrite(CFWriteStreamRef stream, const UInt8* buffer, CFIndex bufferLength, CFStreamError* error, _CFFTPStreamContext* ctxt);
static Boolean _FTPStreamCanWrite(CFWriteStreamRef stream, _CFFTPStreamContext* ctxt);
static void _FTPStreamClose(CFTypeRef stream, _CFFTPStreamContext* ctxt);
static CFTypeRef _FTPStreamCopyProperty(CFTypeRef stream, CFStringRef propertyName, _CFFTPStreamContext* ctxt);
static Boolean _FTPStreamSetProperty(CFTypeRef stream, CFStringRef propertyName, CFTypeRef propertyValue, _CFFTPStreamContext* ctxt);
static void _FTPStreamSchedule(CFTypeRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, _CFFTPStreamContext* ctxt);
static void _FTPStreamUnschedule(CFTypeRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, _CFFTPStreamContext* ctxt);
static const void* _CFFTPNetConnectionContextCreate(CFAllocatorRef alloc, const _CFFTPNetConnectionContext* template);
static void _CFFTPNetConnectionContextFinalize(CFAllocatorRef alloc, const _CFFTPNetConnectionContext* ctxt);
static CFStreamError _FTPConnectionCreateStreams(CFAllocatorRef alloc, const _CFFTPNetConnectionContext* key, CFWriteStreamRef* requestStream, CFReadStreamRef* responseStream);
static void _FTPConnectionRequestStateChanged(_CFFTPStreamContext* ctxt, int newState, CFStreamError *err, _CFNetConnectionRef connection, _CFFTPNetConnectionContext* netCtxt);
static void _FTPConnectionTransmitRequest(_CFFTPStreamContext* ctxt, _CFNetConnectionRef connection, _CFFTPNetConnectionContext* netCtxt);
static void _FTPConnectionReceiveResponse(_CFFTPStreamContext* ctxt, _CFNetConnectionRef connection, _CFFTPNetConnectionContext* netCtxt);
static void _FTPResponseStreamCallBack(_CFFTPStreamContext* ctxt, CFReadStreamRef stream, CFStreamEventType type, _CFNetConnectionRef conn, _CFFTPNetConnectionContext* netCtxt);
static void _FTPRequestStreamCallBack(_CFFTPStreamContext* ctxt, CFWriteStreamRef stream, CFStreamEventType type, _CFNetConnectionRef conn, _CFFTPNetConnectionContext* netCtxt);
static CFArrayRef _FTPRunLoopArrayCallBack(_CFFTPStreamContext *ctxt, _CFNetConnectionRef conn, _CFFTPNetConnectionContext *netCtxt);
static Boolean _IsRoot(CFURLRef url);
static void _FTPConnectionCacheCreate(void);
static void _FTPConnectionCacheExpiration(_CFNetConnectionRef conn, CFDateRef expiration, CFMutableArrayRef list);
static void _SetSOCKS4ProxyInformation(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt, CFURLRef proxyUrl);
static void _SetSOCKS5ProxyInformation(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt, CFURLRef proxyUrl);
static void _StartHTTPRequest(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt, CFStreamError* error, CFURLRef proxyUrl);
static Boolean _ProcessHTTPResponse(_CFFTPStreamContext* ctxt, CFStreamError* error);
static void _RollOverHTTPRequest(_CFFTPStreamContext* ctxt, CFStreamError* error);
static void _CFStreamSocketCreatedCallBack(int fd, void* ctxt);
static void _DataStreamCallBack(CFTypeRef stream, CFStreamEventType type, _CFFTPStreamContext* ctxt);
static void _ReleaseDataReadStream(_CFFTPStreamContext* ctxt);
static void _SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, _CFFTPStreamContext* ctxt);
static void _StreamPropertyApplier(CFTypeRef key, CFTypeRef value, CFTypeRef stream);
static Boolean _PASVAddressParser(const UInt8* buffer, struct sockaddr_in* saddr);
static Boolean _EPSVPortParser(const UInt8* buffer, struct sockaddr_in6* saddr);
static u_char _GetProtocolFamily(_CFFTPStreamContext* ctxt, UInt8* buffer);
static Boolean _CreateListenerForContext(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt);
static void _StartTransfer(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _InvalidateServer(_CFFTPStreamContext* ctxt);
static CFStringRef _CreatePathForContext(CFAllocatorRef alloc, _CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _ReportError(_CFFTPStreamContext* ctxt, CFStreamError* error);
static void _ConnectionComplete(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _WriteCommand(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, CFStringRef cmd);
static void _HandleResponse(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static CFURLRef _ConvertToCFFTPHappyURL(CFURLRef url);
static Boolean _ReadModeBits(const UInt8* str, int* mode);
static Boolean _ReadSize(const UInt8* str, UInt64* size);
static CFStringRef _CFStringCreateCopyWithStrippedHTML(CFAllocatorRef alloc, CFStringRef theString);
static const UInt8* _CFFTPGetDateTimeFunc(CFAllocatorRef alloc, const UInt8* str, CFIndex length, CFDateRef* date);
static void _ProxyStreamCallBack(CFReadStreamRef proxyStream, _CFFTPStreamContext* ctxt);
static CFIndex _FindLine(const UInt8 *buffer, CFIndex bufferLength, const UInt8** start, const UInt8** end);
static void _AdvanceStateMachine(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length, Boolean isMultiLine);
static void _HandleConnect(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length);
static void _HandleUsername(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandlePassword(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandleSystem(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length);
static void _HandleSiteDirStyle(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length);
static void _HandleSiteTruth(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandlePrintWorkingDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length);
static void _HandleType(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandleChangeDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandlePassive(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length);
static void _HandlePort(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandleRestart(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandleStat(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length, Boolean isMultiLine);
static void _HandleSize(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length);
static void _HandleRetrieve(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandleNameList(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandleList(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandleStore(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandleMakeDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandleRemoveDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandleDelete(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandleRenameFrom(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _HandleRenameTo(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
static void _StartProcess(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
#if 0
#pragma mark -
#pragma mark Extern Function Declarations
#endif
extern void _CFSocketStreamCreatePair(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFSocketNativeHandle s,
const CFSocketSignature* sig, CFReadStreamRef* readStream, CFWriteStreamRef* writeStream);
#if 0
#pragma mark -
#pragma mark Globals
#endif
static CFMutableDictionaryRef gFTPConnectionTimeouts = NULL;
static CFNetConnectionCacheRef gFTPConnectionCache = NULL;
static CFSpinLock_t gFTPSpinLock = 0;
static _CFNetConnectionCallBacks* _kFTPConnectionCallBacks = NULL;
#if 0
#pragma mark -
#pragma mark CFStream Callback Functions
#endif
void
_FTPStreamFinalize(CFTypeRef stream, _CFFTPStreamContext* ctxt) {
_FTPStreamClose(stream, ctxt);
CFRelease(ctxt->_url);
if (ctxt->_newUrl)
CFRelease(ctxt->_newUrl);
CFRelease(ctxt->_runloops);
CFRelease(ctxt->_properties);
if (ctxt->_proxies)
CFRelease(ctxt->_proxies);
if (ctxt->_attributes)
CFRelease(ctxt->_attributes);
CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
}
CFStringRef
_FTPStreamCopyDescription(CFTypeRef stream, _CFFTPStreamContext* ctxt) {
return CFStringCreateWithFormat(CFGetAllocator(stream),
NULL,
kCFFTPStreamDescriptionFormat,
(int)stream,
__CFBitIsSet(ctxt->_flags, kFlagBitPerformUpload) ? kCFFTPStreamUploadDescription : kCFFTPStreamDownloadDescription,
ctxt->_url,
ctxt->_flags);
}
Boolean
_FTPStreamOpen(CFTypeRef stream, CFStreamError* error, Boolean* openComplete,
_CFFTPStreamContext* ctxt)
{
CFAllocatorRef alloc = CFGetAllocator(stream);
UInt32 type = kCFNetConnectionTypeFTP;
_CFFTPNetConnectionContext template;
CFTypeRef proxyUrl = NULL;
CFStringRef proxyHost = NULL;
CFBooleanRef persistent = (CFBooleanRef)CFDictionaryGetValue(ctxt->_properties,
kCFStreamPropertyFTPAttemptPersistentConnection);
Boolean usePersistent = (persistent && CFEqual(persistent, kCFBooleanFalse)) ? FALSE : TRUE;
CFStringRef scheme = CFURLCopyScheme(ctxt->_url);
SInt32 port = CFURLGetPortNumber(ctxt->_url);
CFStringRef host = CFURLCopyHostName(ctxt->_url);
if (!ctxt->_proxies) {
CFDictionaryRef info = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPProxy);
if (info) {
CFRetain(info);
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertyFTPProxy);
}
else {
info = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
if (info) {
CFRetain(info);
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
}
}
ctxt->_proxies = _CFNetworkFindProxyForURLAsync(NULL, ctxt->_url,
NULL, info, (_CFProxyStreamCallBack)_ProxyStreamCallBack,
ctxt,
&ctxt->_proxyStream);
if (info)
CFRelease(info);
ctxt->_current = 0;
if (!ctxt->_proxies) {
CFRelease(scheme);
CFRelease(host);
if (ctxt->_proxyStream) {
_CFTypeScheduleOnMultipleRunLoops(ctxt->_proxyStream, ctxt->_runloops);
*openComplete = FALSE;
return TRUE;
}
else {
*openComplete = TRUE;
error->domain = _kCFStreamErrorDomainNativeSockets;
error->error = ENOTCONN;
return FALSE;
}
}
}
if (ctxt->_current == CFArrayGetCount(ctxt->_proxies)) {
CFRelease(scheme);
CFRelease(host);
*openComplete = TRUE;
if (ctxt->_error.error)
*error = ctxt->_error;
else {
error->domain = _kCFStreamErrorDomainNativeSockets;
error->error = ENOTCONN;
}
return FALSE;
}
ctxt->_error.domain = 0;
ctxt->_error.error = 0;
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertyFTPProxy);
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
__CFBitClear(ctxt->_flags, kFlagBitReadHTTPResponse);
__CFBitClear(ctxt->_flags, kFlagBitIsHTTPRequest);
proxyUrl = (CFTypeRef)CFArrayGetValueAtIndex(ctxt->_proxies, ctxt->_current);
if (!CFEqual(proxyUrl, kCFNull)) {
CFStringRef pScheme = CFURLCopyScheme(proxyUrl);
if (CFEqual(pScheme, kFTPSchemeString)) {
CFStringRef pHost = CFURLCopyHostName(proxyUrl);
SInt32 p = CFURLGetPortNumber(proxyUrl);
CFNumberRef pPort = CFNumberCreate(alloc, kCFNumberSInt32Type, &p);
CFMutableDictionaryRef pInfo = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(pInfo, kCFStreamPropertyFTPProxyHost, pHost);
CFDictionaryAddValue(pInfo, kCFStreamPropertyFTPProxyPort, pPort);
CFDictionaryAddValue(ctxt->_properties, kCFStreamPropertyFTPProxy, pInfo);
CFRelease(pInfo);
proxyHost = pHost;
CFRelease(pHost);
CFRelease(pPort);
}
else if (CFEqual(pScheme, kSOCKS4SchemeString))
_SetSOCKS4ProxyInformation(alloc, ctxt, proxyUrl);
else if (CFEqual(pScheme, kSOCKS5SchemeString))
_SetSOCKS5ProxyInformation(alloc, ctxt, proxyUrl);
else if ((CFStringCompare(pScheme, kHTTPSchemeString, kCFCompareCaseInsensitive) == kCFCompareEqualTo) ||
(CFStringCompare(pScheme, kHTTPSSchemeString, kCFCompareCaseInsensitive) == kCFCompareEqualTo))
{
CFRelease(pScheme);
CFRelease(scheme);
if (__CFBitIsSet(ctxt->_flags, kFlagBitPerformUpload)) {
ctxt->_current++;
return _FTPStreamOpen(stream, error, openComplete, ctxt);
}
_StartHTTPRequest(alloc, ctxt, error, proxyUrl);
if (error->error) {
*openComplete = TRUE;
return FALSE;
}
return TRUE;
}
CFRelease(pScheme);
}
if (CFStringCompare(scheme, kFTPSchemeString, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
if (port == -1)
port = 21;
if (proxyHost)
type = kCFNetConnectionTypeFTPProxy;
}
else {
CFTypeRef ssl = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketSecurityLevel);
if (!ssl)
_FTPStreamSetProperty(stream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL, ctxt);
if (port == -1)
port = 990;
if (proxyHost)
type = kCFNetConnectionTypeFTPSProxy;
else
type = kCFNetConnectionTypeFTPS;
}
CFRelease(scheme);
*openComplete = FALSE;
memset(error, 0, sizeof(error[0]));
if (!ctxt->_connection) {
CFMutableArrayRef expired = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
_CFNetConnectionCacheKey key = createConnectionCacheKey(host, port, type, ctxt->_properties);
memset(&template, 0, sizeof(template));
template._state = kFTPStateConnect;
template._key = key;
__CFSpinLock(&gFTPSpinLock);
if (gFTPConnectionCache == NULL)
_FTPConnectionCacheCreate();
__CFSpinUnlock(&gFTPSpinLock);
lockConnectionCache(gFTPConnectionCache);
CFDictionaryApplyFunction( gFTPConnectionTimeouts, (CFDictionaryApplierFunction)_FTPConnectionCacheExpiration, expired);
unlockConnectionCache(gFTPConnectionCache);
ctxt->_connection = findOrCreateNetConnection(gFTPConnectionCache,
alloc,
_kFTPConnectionCallBacks,
&template,
key,
usePersistent,
ctxt->_properties);
lockConnectionCache(gFTPConnectionCache);
CFDictionaryRemoveValue(gFTPConnectionTimeouts, ctxt->_connection);
if (CFArrayGetCount(expired)) {
CFIndex i;
for (i = CFArrayGetCount(expired) - 1; i >= 0; i--) {
_CFNetConnectionRef conn = (_CFNetConnectionRef)CFArrayGetValueAtIndex(expired, i);
CFDictionaryRemoveValue(gFTPConnectionTimeouts, conn);
if (conn != ctxt->_connection)
_CFNetConnectionSetAllowsNewRequests(conn, FALSE);
}
}
CFRelease(expired);
unlockConnectionCache(gFTPConnectionCache);
releaseConnectionCacheKey(key);
}
CFRelease(host);
if (!ctxt->_connection) {
*openComplete = TRUE;
error->error = errno;
if (!error->error)
error->error = ENOMEM;
error->domain = kCFStreamErrorDomainPOSIX;
return FALSE;
}
if (!_CFNetConnectionEnqueue(ctxt->_connection, ctxt)) {
*openComplete = TRUE;
error->error = errno;
if (!error->error)
error->error = ENOMEM;
error->domain = kCFStreamErrorDomainPOSIX;
return FALSE;
} else if (!usePersistent) {
_CFNetConnectionSetAllowsNewRequests(ctxt->_connection, FALSE);
}
return TRUE;
}
Boolean
_FTPStreamOpenCompleted(CFTypeRef stream, CFStreamError* error, _CFFTPStreamContext* ctxt) {
Boolean result = FALSE;
memset(error, 0, sizeof(error[0]));
if (ctxt->_proxyStream)
_ProxyStreamCallBack(ctxt->_proxyStream, ctxt);
if (ctxt->_dataStream) {
CFStreamStatus status;
CFTypeID i = CFReadStreamGetTypeID();
if (CFGetTypeID(ctxt->_dataStream) == i)
status = CFReadStreamGetStatus((CFReadStreamRef)ctxt->_dataStream);
else
status = CFWriteStreamGetStatus((CFWriteStreamRef)ctxt->_dataStream);
switch (status) {
case kCFStreamStatusNotOpen:
case kCFStreamStatusOpening:
break;
case kCFStreamStatusError:
if (CFGetTypeID(stream) == i)
*error = CFReadStreamGetError((CFReadStreamRef)stream);
else
*error = CFWriteStreamGetError((CFWriteStreamRef)stream);
return TRUE;
default:
return TRUE;
}
}
if (ctxt->_connection) {
_CFNetConnectionGetState(ctxt->_connection, TRUE, ctxt);
if (ctxt->_connection &&
(_CFNetConnectionGetCurrentRequest(ctxt->_connection) == ctxt))
{
CFReadStreamRef rStream = _CFNetConnectionGetResponseStream(ctxt->_connection);
CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(ctxt->_connection);
memset(error, 0, sizeof(error[0]));
if (rStream && (CFReadStreamGetStatus(rStream) == kCFStreamStatusError)) {
*error = CFReadStreamGetError(rStream);
if (ctxt->_connection &&
(((_CFFTPNetConnectionContext*)_CFNetConnectionGetInfoPointer(ctxt->_connection))->_state == kFTPStateConnect) &&
(ctxt->_current < CFArrayGetCount(ctxt->_proxies)))
{
ctxt->_current++;
ctxt->_error = *error;
_CFNetConnectionErrorOccurred(ctxt->_connection, error);
return result;
}
else {
result = TRUE;
_ReportError(ctxt, error);
}
}
else if (wStream && (CFWriteStreamGetStatus(wStream) == kCFStreamStatusError)) {
*error = CFWriteStreamGetError(wStream);
if (ctxt->_connection &&
(((_CFFTPNetConnectionContext*)_CFNetConnectionGetInfoPointer(ctxt->_connection))->_state == kFTPStateConnect) &&
(ctxt->_current < CFArrayGetCount(ctxt->_proxies)))
{
ctxt->_current++;
ctxt->_error = *error;
_CFNetConnectionErrorOccurred(ctxt->_connection, error);
return result;
}
else {
result = TRUE;
_ReportError(ctxt, error);
}
}
}
}
if (ctxt->_server) {
CFRunLoopSourceRef src = CFSocketCreateRunLoopSource(CFGetAllocator(ctxt->_server), ctxt->_server, 0);
if (src) {
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFRunLoopAddSource(rl, src, kCFFTPStreamOpenCompleted);
CFRunLoopRunInMode(kCFFTPStreamOpenCompleted, 0.0, TRUE);
CFRunLoopRemoveSource(rl, src, kCFFTPStreamOpenCompleted);
CFRelease(src);
}
}
return result;
}
CFIndex
_FTPStreamRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error,
Boolean* atEOF, _CFFTPStreamContext* ctxt)
{
CFIndex result = 0;
*atEOF = FALSE;
memset(error, 0, sizeof(error[0]));
if (ctxt->_proxyStream) {
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFReadStreamRef s = (CFReadStreamRef)CFRetain(ctxt->_proxyStream);
CFReadStreamScheduleWithRunLoop(s, rl, kCFFTPStreamOpenCompleted);
do {
CFRunLoopRunInMode(kCFFTPStreamOpenCompleted, 1e+20, TRUE);
} while (ctxt->_proxyStream);
CFReadStreamUnscheduleFromRunLoop(s, rl, kCFFTPStreamOpenCompleted);
CFRelease(s);
}
while (ctxt->_connection && (!ctxt->_dataStream || !CFReadStreamHasBytesAvailable((CFReadStreamRef)ctxt->_dataStream))) {
CFWriteStreamRef requestStreams;
CFReadStreamRef responseStreams;
_CFNetConnectionGetState(ctxt->_connection, TRUE, ctxt);
if (!ctxt->_connection) {
*error = CFReadStreamGetError((CFReadStreamRef)ctxt->_userStream);
if (error->error) {
*atEOF = TRUE;
result = -1;
}
break;
}
else {
requestStreams = _CFNetConnectionGetRequestStream(ctxt->_connection);
responseStreams = _CFNetConnectionGetResponseStream(ctxt->_connection);
if (responseStreams) {
*error = CFReadStreamGetError(responseStreams);
}
if (!error->error && requestStreams) {
*error = CFWriteStreamGetError(requestStreams);
}
if (error->error) {
if ((((_CFFTPNetConnectionContext*)_CFNetConnectionGetInfoPointer(ctxt->_connection))->_state == kFTPStateConnect) &&
(ctxt->_current < CFArrayGetCount(ctxt->_proxies)))
{
ctxt->_current++;
ctxt->_error = *error;
_CFNetConnectionErrorOccurred(ctxt->_connection, error);
continue;
}
else {
result = -1;
*atEOF = TRUE;
break;
}
}
}
}
if (ctxt->_dataStream) {
result = CFReadStreamRead((CFReadStreamRef)ctxt->_dataStream, buffer, bufferLength);
if (__CFBitIsSet(ctxt->_flags, kFlagBitIsHTTPRequest) && !__CFBitIsSet(ctxt->_flags, kFlagBitReadHTTPResponse)) {
if ((result >= 0) && _ProcessHTTPResponse(ctxt, error)) {
if (error->error) {
*atEOF = TRUE; return result; }
else if (__CFBitIsSet(ctxt->_flags, kFlagBit407TriedOnce)) {
return _FTPStreamRead((CFReadStreamRef)ctxt->_userStream, buffer, bufferLength, error, atEOF, ctxt);
}
}
else if (ctxt->_current < CFArrayGetCount(ctxt->_proxies)) {
_RollOverHTTPRequest(ctxt, error);
if (error->error) {
*atEOF = TRUE; return result; }
else {
return _FTPStreamRead((CFReadStreamRef)ctxt->_userStream, buffer, bufferLength, error, atEOF, ctxt);
}
}
}
if (result <= 0) {
*atEOF = TRUE;
*error = CFReadStreamGetError((CFReadStreamRef)ctxt->_dataStream);
}
}
return result;
}
Boolean
_FTPStreamCanRead(CFReadStreamRef stream, _CFFTPStreamContext* ctxt) {
Boolean result = FALSE;
if (ctxt->_proxyStream)
_ProxyStreamCallBack(ctxt->_proxyStream, ctxt);
if (ctxt->_connection) {
_CFNetConnectionGetState(ctxt->_connection, TRUE, ctxt);
if (!ctxt->_connection) {
CFStreamError error = CFReadStreamGetError((CFReadStreamRef)ctxt->_userStream);
if (error.error) {
CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, &error);
result = TRUE;
}
}
else {
CFStreamError error;
CFWriteStreamRef requestStreams = _CFNetConnectionGetRequestStream(ctxt->_connection);
CFReadStreamRef responseStreams = _CFNetConnectionGetResponseStream(ctxt->_connection);
if (responseStreams) {
error = CFReadStreamGetError(responseStreams);
}
if (!error.error && requestStreams) {
error = CFWriteStreamGetError(requestStreams);
}
if (error.error) {
if ((((_CFFTPNetConnectionContext*)_CFNetConnectionGetInfoPointer(ctxt->_connection))->_state == kFTPStateConnect) &&
(ctxt->_current < CFArrayGetCount(ctxt->_proxies)))
{
ctxt->_current++;
ctxt->_error = error;
_CFNetConnectionErrorOccurred(ctxt->_connection, &error);
result = FALSE;
}
else {
CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, &error);
result = TRUE;
}
}
}
}
if (ctxt->_dataStream) {
result = CFReadStreamHasBytesAvailable((CFReadStreamRef)ctxt->_dataStream);
if (!result && CFReadStreamGetStatus((CFReadStreamRef)ctxt->_dataStream) == kCFStreamStatusAtEnd)
result = TRUE;
if (result && __CFBitIsSet(ctxt->_flags, kFlagBitIsHTTPRequest) && !__CFBitIsSet(ctxt->_flags, kFlagBitReadHTTPResponse)) {
CFStreamError error;
if (_ProcessHTTPResponse(ctxt, &error)) {
if (error.error)
CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, &error);
else
result = FALSE;
}
else if (ctxt->_current < CFArrayGetCount(ctxt->_proxies)) {
_RollOverHTTPRequest(ctxt, &error);
if (error.error)
CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, &error);
else
result = FALSE;
}
}
}
return result;
}
CFIndex
_FTPStreamWrite(CFWriteStreamRef stream, const UInt8* buffer, CFIndex bufferLength,
CFStreamError* error, _CFFTPStreamContext* ctxt)
{
CFIndex result = 0;
memset(error, 0, sizeof(error[0]));
if (ctxt->_proxyStream) {
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFReadStreamRef s = (CFReadStreamRef)CFRetain(ctxt->_proxyStream);
CFReadStreamScheduleWithRunLoop(s, rl, kCFFTPStreamOpenCompleted);
do {
CFRunLoopRunInMode(kCFFTPStreamOpenCompleted, 1e+20, TRUE);
} while (ctxt->_proxyStream);
CFReadStreamUnscheduleFromRunLoop(s, rl, kCFFTPStreamOpenCompleted);
CFRelease(s);
}
while (ctxt->_connection && (!ctxt->_dataStream || !CFWriteStreamCanAcceptBytes((CFWriteStreamRef)ctxt->_dataStream))) {
CFWriteStreamRef requestStreams;
CFReadStreamRef responseStreams;
_CFNetConnectionGetState(ctxt->_connection, TRUE, ctxt);
if (!ctxt->_connection) {
*error = CFWriteStreamGetError((CFWriteStreamRef)ctxt->_userStream);
if (error->error)
result = -1;
break;
}
else {
requestStreams = _CFNetConnectionGetRequestStream(ctxt->_connection);
responseStreams = _CFNetConnectionGetResponseStream(ctxt->_connection);
if (responseStreams) {
*error = CFReadStreamGetError(responseStreams);
}
if (!error->error && requestStreams) {
*error = CFWriteStreamGetError(requestStreams);
}
if (error->error) {
result = -1;
break;
}
}
}
if (ctxt->_dataStream) {
result = CFWriteStreamWrite((CFWriteStreamRef)ctxt->_dataStream, buffer, bufferLength);
if (result <= 0) {
*error = CFWriteStreamGetError((CFWriteStreamRef)ctxt->_dataStream);
}
}
return result;
}
Boolean
_FTPStreamCanWrite(CFWriteStreamRef stream, _CFFTPStreamContext* ctxt) {
Boolean result = FALSE;
if (ctxt->_proxyStream)
_ProxyStreamCallBack(ctxt->_proxyStream, ctxt);
if (ctxt->_connection) {
_CFNetConnectionGetState(ctxt->_connection, TRUE, ctxt);
if (!ctxt->_connection) {
CFStreamError error = CFWriteStreamGetError((CFWriteStreamRef)ctxt->_userStream);
if (error.error) {
CFWriteStreamSignalEvent((CFWriteStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, &error);
result = TRUE;
}
}
else {
CFStreamError error;
CFWriteStreamRef requestStreams = _CFNetConnectionGetRequestStream(ctxt->_connection);
CFReadStreamRef responseStreams = _CFNetConnectionGetResponseStream(ctxt->_connection);
if (responseStreams) {
error = CFReadStreamGetError(responseStreams);
}
if (!error.error && requestStreams) {
error = CFWriteStreamGetError(requestStreams);
}
if (error.error) {
if ((((_CFFTPNetConnectionContext*)_CFNetConnectionGetInfoPointer(ctxt->_connection))->_state == kFTPStateConnect) &&
(ctxt->_current < CFArrayGetCount(ctxt->_proxies)))
{
ctxt->_current++;
ctxt->_error = error;
_CFNetConnectionErrorOccurred(ctxt->_connection, &error);
result = FALSE;
}
else {
CFWriteStreamSignalEvent((CFWriteStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, &error);
result = TRUE;
}
}
}
}
if (ctxt->_dataStream)
return CFWriteStreamCanAcceptBytes((CFWriteStreamRef)ctxt->_dataStream);
return FALSE;
}
void
_FTPStreamClose(CFTypeRef stream, _CFFTPStreamContext* ctxt) {
_InvalidateServer(ctxt);
if (ctxt->_proxyStream) {
_CFTypeUnscheduleFromMultipleRunLoops(ctxt->_proxyStream, ctxt->_runloops);
CFReadStreamClose(ctxt->_proxyStream);
CFRelease(ctxt->_proxyStream);
ctxt->_proxyStream = NULL;
}
if (ctxt->_dataStream) {
if (CFGetTypeID(ctxt->_dataStream) == CFReadStreamGetTypeID())
_ReleaseDataReadStream(ctxt);
else {
_CFTypeInvalidate(ctxt->_dataStream);
_CFTypeUnscheduleFromMultipleRunLoops(ctxt->_dataStream, ctxt->_runloops);
CFWriteStreamClose((CFWriteStreamRef)(ctxt->_dataStream));
CFRelease(ctxt->_dataStream);
ctxt->_dataStream = NULL;
}
}
if (ctxt->_connection) {
int state = _CFNetConnectionGetState(ctxt->_connection, FALSE, ctxt);
if (state != kTransmittingRequest)
_CFNetConnectionDequeue(ctxt->_connection, ctxt);
else {
CFArrayRef a = ctxt->_runloops;
int i, count = CFArrayGetCount(a);
for (i = 0; i < count; i += 2) {
_CFNetConnectionUnschedule(ctxt->_connection,
ctxt,
(CFRunLoopRef)CFArrayGetValueAtIndex(a, i),
(CFStringRef)CFArrayGetValueAtIndex(a, i + 1));
}
_CFNetConnectionRequestIsComplete(ctxt->_connection, ctxt);
_CFNetConnectionResponseIsComplete(ctxt->_connection, ctxt);
}
}
}
CFTypeRef
_FTPStreamCopyProperty(CFTypeRef stream, CFStringRef propertyName, _CFFTPStreamContext* ctxt) {
CFTypeRef value = NULL;
if (CFEqual(propertyName, kCFStreamPropertyFTPUsePassiveMode)) {
value = CFRetain(__CFBitIsSet(ctxt->_flags, kFlagBitPerformPASV) ? kCFBooleanTrue : kCFBooleanFalse);
}
else if (CFEqual(propertyName, kCFStreamPropertyFTPFetchResourceInfo)) {
value = CFRetain(__CFBitIsSet(ctxt->_flags, kFlagBitPerformSTAT) ? kCFBooleanTrue : kCFBooleanFalse);
}
else if (CFEqual(propertyName, kCFStreamPropertyFTPFetchNameList)) {
value = CFRetain(__CFBitIsSet(ctxt->_flags, kFlagBitPerformNLST) ? kCFBooleanTrue : kCFBooleanFalse);
}
else if (CFEqual(propertyName, kCFStreamPropertyFTPFileTransferOffset)) {
value = CFNumberCreate(CFGetAllocator(ctxt->_properties), kCFNumberLongLongType, &ctxt->_offset);
}
else if (CFEqual(propertyName, kCFStreamPropertyFTPResourceSize)) {
if (ctxt->_attributes) {
CFNumberRef original = CFDictionaryGetValue(ctxt->_attributes, kCFFTPResourceSize);
if (original)
value = CFRetain(original); }
}
else if (CFEqual(propertyName, kCFStreamPropertyFTPAttemptPersistentConnection)) {
CFBooleanRef contains = CFDictionaryGetValue(ctxt->_properties, propertyName);
value = contains ? CFRetain(contains) : CFRetain(kCFBooleanTrue);
}
else if (CFEqual(propertyName, _kCFStreamPropertyFTPLogInOnly)) {
value = CFRetain(__CFBitIsSet(ctxt->_flags, kFlagBitLogInOnly) ? kCFBooleanTrue : kCFBooleanFalse);
}
else if (CFEqual(propertyName, _kCFStreamPropertyFTPRemoveResource)) {
value = CFRetain(__CFBitIsSet(ctxt->_flags, kFlagBitRemoveResource) ? kCFBooleanTrue : kCFBooleanFalse);
}
else if (CFEqual(propertyName, _kCFStreamPropertyFTPNewResourceName)) {
value = ctxt->_newUrl ? CFRetain(ctxt->_newUrl) : NULL;
}
if (!value && ctxt->_dataStream) {
if (CFGetTypeID(ctxt->_dataStream) == CFReadStreamGetTypeID())
value = CFReadStreamCopyProperty((CFReadStreamRef)ctxt->_dataStream, propertyName);
else
value = CFWriteStreamCopyProperty((CFWriteStreamRef)ctxt->_dataStream, propertyName);
}
if (!value && ctxt->_connection &&
(_CFNetConnectionGetCurrentRequest(ctxt->_connection) == ctxt))
{
CFTypeRef s = _CFNetConnectionGetResponseStream(ctxt->_connection);
if (s)
value = CFReadStreamCopyProperty((CFReadStreamRef)s, propertyName);
if (!value) {
s = _CFNetConnectionGetRequestStream(ctxt->_connection);
if (s)
value = CFWriteStreamCopyProperty((CFWriteStreamRef)s, propertyName);
}
}
if (!value) {
CFTypeRef orig = CFDictionaryGetValue(ctxt->_properties, propertyName);
if (orig) {
CFTypeID i = CFGetTypeID(orig);
if (i == CFStringGetTypeID())
value = CFStringCreateCopy(CFGetAllocator(ctxt->_properties), orig);
else if (i == CFDataGetTypeID())
value = CFDataCreateCopy(CFGetAllocator(ctxt->_properties), orig);
else if (i == CFDictionaryGetTypeID())
value = CFDictionaryCreateCopy(CFGetAllocator(ctxt->_properties), orig);
else if (i == CFArrayGetTypeID())
value = CFArrayCreateCopy(CFGetAllocator(ctxt->_properties), orig);
}
}
return value;
}
Boolean
_FTPStreamSetProperty(CFTypeRef stream, CFStringRef propertyName,
CFTypeRef propertyValue, _CFFTPStreamContext* ctxt)
{
Boolean result = FALSE;
if (CFEqual(propertyName, kCFStreamPropertyFTPProxy)) {
if (!ctxt->_connection && !ctxt->_dataStream) {
if (!propertyValue) {
CFDictionaryRemoveValue(ctxt->_properties, propertyName);
result = TRUE;
}
else if (CFGetTypeID(propertyValue) == CFDictionaryGetTypeID()) {
CFTypeRef p = CFDictionaryGetValue(propertyValue, kSCPropNetProxiesFTPPassive);
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
if (!__CFBitIsSet(ctxt->_flags, kFlagBitDidSetPassiveBit) && p) {
if (CFGetTypeID(p) == CFNumberGetTypeID()) {
SInt32 val;
CFNumberGetValue(p, kCFNumberSInt32Type, &val);
if (val)
__CFBitSet(ctxt->_flags, kFlagBitPerformPASV);
else
__CFBitClear(ctxt->_flags, kFlagBitPerformPASV);
}
else if (p == kCFBooleanFalse) {
__CFBitClear(ctxt->_flags, kFlagBitPerformPASV);
}
}
result = TRUE;
}
}
}
else if (CFEqual(propertyName, kCFStreamPropertySOCKSProxy)) {
if (!ctxt->_connection && !ctxt->_dataStream) {
if (!propertyValue) {
CFDictionaryRemoveValue(ctxt->_properties, propertyName);
result = TRUE;
}
else if (CFGetTypeID(propertyValue) == CFDictionaryGetTypeID()) {
if (!CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPProxy)) {
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertyFTPProxy);
CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
result = TRUE;
}
}
}
}
else if (CFEqual(propertyName, kCFStreamPropertyFTPUsePassiveMode)) {
if (propertyValue && CFEqual(propertyValue, kCFBooleanFalse))
__CFBitClear(ctxt->_flags, kFlagBitPerformPASV);
else
__CFBitSet(ctxt->_flags, kFlagBitPerformPASV);
__CFBitSet(ctxt->_flags, kFlagBitDidSetPassiveBit);
result = TRUE;
}
else if (CFEqual(propertyName, kCFStreamPropertyFTPFetchResourceInfo)) {
if (propertyValue && CFEqual(propertyValue, kCFBooleanTrue))
__CFBitSet(ctxt->_flags, kFlagBitPerformSTAT);
else
__CFBitClear(ctxt->_flags, kFlagBitPerformSTAT);
result = TRUE;
}
else if (CFEqual(propertyName, kCFStreamPropertyFTPFetchNameList)) {
if (propertyValue && CFEqual(propertyValue, kCFBooleanTrue))
__CFBitSet(ctxt->_flags, kFlagBitPerformNLST);
else
__CFBitClear(ctxt->_flags, kFlagBitPerformNLST);
result = TRUE;
}
else if (CFEqual(propertyName, kCFStreamPropertyFTPFileTransferOffset)) {
if (!propertyValue)
ctxt->_offset = 0;
else
CFNumberGetValue(propertyValue, kCFNumberLongLongType, &ctxt->_offset);
result = TRUE;
}
else if (CFEqual(propertyName, _kCFStreamPropertyFTPLogInOnly)) {
if (propertyValue && CFEqual(propertyValue, kCFBooleanTrue))
__CFBitSet(ctxt->_flags, kFlagBitLogInOnly);
else
__CFBitClear(ctxt->_flags, kFlagBitLogInOnly);
result = TRUE;
}
else if (CFEqual(propertyName, _kCFStreamPropertyFTPRemoveResource)) {
if (propertyValue && CFEqual(propertyValue, kCFBooleanTrue))
__CFBitSet(ctxt->_flags, kFlagBitRemoveResource);
else
__CFBitClear(ctxt->_flags, kFlagBitRemoveResource);
result = TRUE;
}
else if (CFEqual(propertyName, _kCFStreamPropertyFTPNewResourceName)) {
CFURLRef temp = ctxt->_newUrl;
ctxt->_newUrl = propertyValue ? CFURLCopyAbsoluteURL(propertyValue) : NULL;
if (temp)
CFRelease(temp);
result = TRUE;
}
else if (CFEqual(propertyName, kCFStreamPropertyFTPResourceSize))
result = FALSE;
else if (CFEqual(propertyName, kCFStreamPropertyFTPAttemptPersistentConnection)) {
if (!propertyValue || !CFEqual(propertyValue, kCFBooleanFalse))
CFDictionaryRemoveValue(ctxt->_properties, propertyName);
else
CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
}
else {
if (ctxt->_dataStream) {
if (CFGetTypeID(ctxt->_dataStream) == CFReadStreamGetTypeID())
result = CFReadStreamSetProperty((CFReadStreamRef)ctxt->_dataStream, propertyName, propertyValue);
else
result = CFWriteStreamSetProperty((CFWriteStreamRef)ctxt->_dataStream, propertyName, propertyValue);
}
if (!result) {
if (propertyValue)
CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
else
CFDictionaryRemoveValue(ctxt->_properties, propertyName);
result = TRUE;
}
}
return result;
}
void
_FTPStreamSchedule(CFTypeRef stream, CFRunLoopRef runLoop,
CFStringRef runLoopMode, _CFFTPStreamContext* ctxt)
{
if (_SchedulesAddRunLoopAndMode(ctxt->_runloops, runLoop, runLoopMode)) {
if (ctxt->_proxyStream)
CFReadStreamScheduleWithRunLoop(ctxt->_proxyStream, runLoop, runLoopMode);
if (ctxt->_server)
_CFTypeScheduleOnRunLoop(ctxt->_server, runLoop, runLoopMode);
if (ctxt->_dataStream)
_CFTypeScheduleOnRunLoop(ctxt->_dataStream, runLoop, runLoopMode);
if (ctxt->_connection)
_CFNetConnectionSchedule(ctxt->_connection, ctxt, runLoop, runLoopMode);
}
}
void
_FTPStreamUnschedule(CFTypeRef stream, CFRunLoopRef runLoop,
CFStringRef runLoopMode, _CFFTPStreamContext* ctxt)
{
if (_SchedulesRemoveRunLoopAndMode(ctxt->_runloops, runLoop, runLoopMode)) {
if (ctxt->_proxyStream)
CFReadStreamUnscheduleFromRunLoop(ctxt->_proxyStream, runLoop, runLoopMode);
if (ctxt->_server)
_CFTypeUnscheduleFromRunLoop(ctxt->_server, runLoop, runLoopMode);
if (ctxt->_dataStream)
_CFTypeUnscheduleFromRunLoop(ctxt->_dataStream, runLoop, runLoopMode);
if (ctxt->_connection)
_CFNetConnectionUnschedule(ctxt->_connection, ctxt, runLoop, runLoopMode);
}
}
#if 0
#pragma mark -
#pragma mark CFNetConnection Callback Functions
#endif
const void*
_CFFTPNetConnectionContextCreate(CFAllocatorRef alloc, const _CFFTPNetConnectionContext* template) {
_CFFTPNetConnectionContext* ctxt = (_CFFTPNetConnectionContext*)CFAllocatorAllocate(alloc,
sizeof(ctxt[0]),
0);
memmove(ctxt, template, sizeof(ctxt[0]));
ctxt->_key = (_CFNetConnectionCacheKey)connCacheKeyRetain(alloc, template->_key);
if (!ctxt->_recvBuffer)
ctxt->_recvBuffer = CFDataCreateMutable(alloc, 0);
if (!ctxt->_sendBuffer)
ctxt->_sendBuffer = CFDataCreateMutable(alloc, 0);
__CFBitClear(ctxt->_flags, kFlagBitIsXServer);
return (const void*)ctxt;
}
void
_CFFTPNetConnectionContextFinalize(CFAllocatorRef alloc, const _CFFTPNetConnectionContext* ctxt) {
connCacheKeyRelease(alloc, ctxt->_key);
if (ctxt->_root)
CFRelease(ctxt->_root);
if (ctxt->_recvBuffer)
CFRelease(ctxt->_recvBuffer);
if (ctxt->_sendBuffer)
CFRelease(ctxt->_sendBuffer);
CFAllocatorDeallocate(alloc, (_CFFTPNetConnectionContext*)ctxt);
}
CFStreamError
_FTPConnectionCreateStreams(CFAllocatorRef alloc,
const _CFFTPNetConnectionContext* ctxt,
CFWriteStreamRef* requestStream,
CFReadStreamRef* responseStream)
{
CFStreamError result = {0, 0};
UInt32 type;
SInt32 port;
CFStringRef host;
CFDictionaryRef properties;
getValuesFromKey(ctxt->_key, &host, &port, &type, &properties);
*requestStream = NULL;
*responseStream = NULL;
if ((type == kCFNetConnectionTypeFTPProxy) || (type == kCFNetConnectionTypeFTPSProxy)) {
CFDictionaryRef proxy = CFDictionaryGetValue(properties, kCFStreamPropertyFTPProxy);
CFNumberRef cfport = CFDictionaryGetValue(proxy, kCFStreamPropertyFTPProxyPort);
host = CFDictionaryGetValue(proxy, kCFStreamPropertyFTPProxyHost);
if (cfport)
CFNumberGetValue(cfport, kCFNumberSInt32Type, &port);
else if (type == kCFNetConnectionTypeFTPProxy)
port = 21;
else
port = 990;
}
_CFSocketStreamCreatePair(alloc, host, port, 0, NULL, responseStream, requestStream);
if (*responseStream && *requestStream) {
CFArrayCallBacks cb = {0, NULL, NULL, NULL, NULL};
const void* values[2] = {_CFStreamSocketCreatedCallBack, NULL};
CFArrayRef callback = CFArrayCreate(alloc, values, sizeof(values) / sizeof(values[0]), &cb);
if (callback) {
CFWriteStreamSetProperty(*requestStream, CFSTR("_kCFStreamSocketCreatedCallBack"), callback);
CFRelease(callback);
}
CFDictionaryApplyFunction(properties, (CFDictionaryApplierFunction)_StreamPropertyApplier, *responseStream);
CFDictionaryApplyFunction(properties, (CFDictionaryApplierFunction)_StreamPropertyApplier, *requestStream);
}
else {
result.domain = kCFStreamErrorDomainPOSIX;
result.error = errno;
#if defined(__WIN32__)
if (!result.error) {
result.error = WSAGetLastError();
if (result.error)
result.domain = kCFStreamErrorDomainWinSock;
}
#endif
if (!result.error)
result.error = ENOMEM;
}
return result;
}
void
_FTPConnectionRequestStateChanged(_CFFTPStreamContext* ctxt, int newState,
CFStreamError *err, _CFNetConnectionRef connection,
_CFFTPNetConnectionContext* netCtxt)
{
switch (newState) {
case kQueued:
ctxt->_connection = connection;
break;
case kTransmittingRequest:
{
CFArrayRef a = ctxt->_runloops;
int i, count = CFArrayGetCount(a);
for (i = 0; i < count; i += 2) {
_CFNetConnectionSchedule(connection,
ctxt,
(CFRunLoopRef)CFArrayGetValueAtIndex(a, i),
(CFStringRef)CFArrayGetValueAtIndex(a, i + 1));
}
if (netCtxt->_state == kFTPStateIdle)
_StartProcess(netCtxt, ctxt);
else if (netCtxt->_state > kFTPStateIdle) {
__CFBitSet(netCtxt->_flags, kFlagBitReturnToIdle);
_CFNetConnectionGetState(ctxt->_connection, TRUE, ctxt);
}
}
break;
case kFinished:
{
CFBooleanRef persistent = (CFBooleanRef)CFDictionaryGetValue(ctxt->_properties,
kCFStreamPropertyFTPAttemptPersistentConnection);
Boolean usePersistent = (persistent && CFEqual(persistent, kCFBooleanFalse)) ? FALSE : TRUE;
__CFBitSet(netCtxt->_flags, kFlagBitLeftForDead);
if (usePersistent)
CFDictionarySetValue(gFTPConnectionTimeouts, ctxt->_connection, CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + kFTPTimeoutInSeconds));
}
case kOrphaned:
{
CFArrayRef a = ctxt->_runloops;
int i, count = CFArrayGetCount(a);
_CFNetConnectionDequeue(connection, ctxt);
for (i = 0; i < count; i += 2) {
_CFNetConnectionUnschedule(connection,
ctxt,
(CFRunLoopRef)CFArrayGetValueAtIndex(a, i),
(CFStringRef)CFArrayGetValueAtIndex(a, i + 1));
}
CFRelease(ctxt->_connection);
ctxt->_connection = NULL;
if ((newState == kOrphaned) && !__CFBitIsSet(ctxt->_flags, kFlagBitGotError)) {
CFStreamError error;
Boolean open;
_FTPStreamOpen(ctxt->_userStream, &error, &open, ctxt);
if (open) {
CFStreamEventType event = kCFStreamEventErrorOccurred;
if (!error.error)
event = kCFStreamEventOpenCompleted;
if (CFGetTypeID(ctxt->_userStream) == CFReadStreamGetTypeID())
CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, event, &error);
else
CFWriteStreamSignalEvent((CFWriteStreamRef)ctxt->_userStream, event, &error);
}
}
}
break;
default:
break;
}
}
void
_FTPConnectionTransmitRequest(_CFFTPStreamContext* ctxt, _CFNetConnectionRef connection, _CFFTPNetConnectionContext* netCtxt) {
CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(connection);
CFReadStreamRef rStream = _CFNetConnectionGetResponseStream(connection);
if (CFWriteStreamCanAcceptBytes(wStream)) {
if (__CFBitIsSet(netCtxt->_flags, kFlagBitHTTPLitmus))
_FTPRequestStreamCallBack(ctxt, wStream, kCFStreamEventCanAcceptBytes, connection, netCtxt);
else {
CFStringRef cmd;
CFStringRef user = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPUserName);
CFStringRef host;
SInt32 port;
UInt32 type;
CFDictionaryRef properties;
CFAllocatorRef alloc = CFGetAllocator(ctxt->_runloops);
getValuesFromKey(netCtxt->_key, &host, &port, &type, &properties);
if (user)
CFRetain(user);
else {
user = CFURLCopyUserName(ctxt->_url);
if (!user)
user = CFRetain(kAnonymousUserString);
}
if ((type == kCFNetConnectionTypeFTPProxy) || (type == kCFNetConnectionTypeFTPSProxy)) {
CFStringRef newUser;
if (((type == kCFNetConnectionTypeFTPProxy) && (port == 21)) ||
((type == kCFNetConnectionTypeFTPSProxy) && (port == 990)))
{
newUser = CFStringCreateWithFormat(alloc, NULL, kFTPProxyFormat, user, host);
}
else
newUser = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@@%@:%ld"), user, host, port);
CFRelease(user);
user = newUser;
}
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPUSERCommandString, user);
CFRelease(user);
__CFBitSet(netCtxt->_flags, kFlagBitHTTPLitmus);
if (cmd) {
_WriteCommand(netCtxt, ctxt, cmd);
CFRelease(cmd);
}
else {
CFStreamError error = {kCFStreamErrorDomainPOSIX, errno};
if (!error.error)
error.error = ENOMEM;
_ReportError(ctxt, &error);
}
}
}
if (ctxt->_connection && CFReadStreamHasBytesAvailable(rStream))
_FTPConnectionReceiveResponse(ctxt, connection, netCtxt);
}
void
_FTPConnectionReceiveResponse(_CFFTPStreamContext* ctxt, _CFNetConnectionRef connection, _CFFTPNetConnectionContext* netCtxt) {
CFReadStreamRef rStream = _CFNetConnectionGetResponseStream(connection);
CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(connection);
if (CFReadStreamHasBytesAvailable(rStream))
_FTPResponseStreamCallBack(ctxt, rStream, kCFStreamEventHasBytesAvailable, connection, netCtxt);
if (ctxt->_connection && CFWriteStreamCanAcceptBytes(wStream))
_FTPConnectionTransmitRequest(ctxt, connection, netCtxt);
}
void
_FTPResponseStreamCallBack(_CFFTPStreamContext* ctxt, CFReadStreamRef stream,
CFStreamEventType type, _CFNetConnectionRef conn,
_CFFTPNetConnectionContext* netCtxt)
{
switch (type) {
case kCFStreamEventHasBytesAvailable:
{
CFIndex i, canRead = CFDataGetLength(netCtxt->_recvBuffer) - netCtxt->_recvCount;
if (!CFReadStreamHasBytesAvailable(stream))
return;
if (canRead < kBufferGrowthSize) {
CFDataSetLength(netCtxt->_recvBuffer, netCtxt->_recvCount + kBufferGrowthSize);
if (CFDataGetLength(netCtxt->_recvBuffer) < (netCtxt->_recvCount + kBufferGrowthSize)) {
CFStreamError error = {kCFStreamErrorDomainPOSIX, ENOMEM};
_ReportError(ctxt, &error);
return;
}
canRead = kBufferGrowthSize;
}
i = CFReadStreamRead(stream,
CFDataGetMutableBytePtr(netCtxt->_recvBuffer) + netCtxt->_recvCount,
canRead);
if (i < 0)
_FTPResponseStreamCallBack(ctxt, stream, kCFStreamEventErrorOccurred, conn, netCtxt);
else if (!i) {
if (__CFBitIsSet(netCtxt->_flags, kFlagBitLeftForDead)) {
CFStreamError error;
Boolean openComplete;
_CFNetConnectionLost(ctxt->_connection);
_CFNetConnectionDequeue(ctxt->_connection, ctxt);
CFRelease(ctxt->_connection);
ctxt->_connection = NULL;
_FTPStreamOpen(ctxt->_userStream, &error, &openComplete, ctxt);
}
else
_FTPResponseStreamCallBack(ctxt, stream, kCFStreamEventEndEncountered, conn, netCtxt);
}
else {
netCtxt->_recvCount += i;
_HandleResponse(netCtxt, ctxt);
}
}
break;
case kCFStreamEventErrorOccurred:
{
CFStreamError error = CFReadStreamGetError(stream);
_ReportError(ctxt, &error);
}
break;
case kCFStreamEventEndEncountered:
{
CFStreamError error = {_kCFStreamErrorDomainNativeSockets, ENOTCONN};
_ReportError(ctxt, &error);
}
break;
default:
break;
}
}
void
_FTPRequestStreamCallBack(_CFFTPStreamContext* ctxt, CFWriteStreamRef stream,
CFStreamEventType type, _CFNetConnectionRef conn,
_CFFTPNetConnectionContext* netCtxt)
{
switch (type) {
case kCFStreamEventCanAcceptBytes:
{
if (!__CFBitIsSet(netCtxt->_flags, kFlagBitHTTPLitmus)) {
_FTPConnectionTransmitRequest(ctxt, conn, netCtxt); break;
}
if (netCtxt->_sendCount) {
UInt8* buffer = CFDataGetMutableBytePtr(netCtxt->_sendBuffer);
CFIndex i = CFWriteStreamWrite(stream, buffer, netCtxt->_sendCount);
if (i < 0)
_FTPRequestStreamCallBack(ctxt, stream, kCFStreamEventErrorOccurred, conn, netCtxt);
else if (!i)
_FTPRequestStreamCallBack(ctxt, stream, kCFStreamEventEndEncountered, conn, netCtxt);
else {
netCtxt->_sendCount -= i;
memmove(buffer, buffer + i, netCtxt->_sendCount);
}
}
}
break;
case kCFStreamEventErrorOccurred:
{
CFStreamError error = CFWriteStreamGetError(stream);
_ReportError(ctxt, &error);
}
break;
case kCFStreamEventEndEncountered:
{
CFStreamError error = {_kCFStreamErrorDomainNativeSockets, ENOTCONN};
_ReportError(ctxt, &error);
}
break;
default:
break;
}
}
CFArrayRef
_FTPRunLoopArrayCallBack(_CFFTPStreamContext *ctxt, _CFNetConnectionRef conn, _CFFTPNetConnectionContext *netCtxt) {
return ctxt->_runloops;
}
#if 0
#pragma mark -
#pragma mark Utility Functions
#endif
Boolean
_IsRoot(CFURLRef url) {
Boolean isAbsolute;
CFStringRef strictPath, resourceSpecifier;
strictPath = CFURLCopyStrictPath(url, &isAbsolute);
resourceSpecifier = CFURLCopyResourceSpecifier(url);
if (!strictPath && !resourceSpecifier) return TRUE;
if (strictPath) CFRelease(strictPath);
if (resourceSpecifier) CFRelease(resourceSpecifier);
return FALSE;
}
void
_FTPConnectionCacheCreate(void) {
if (!_kFTPConnectionCallBacks) {
_kFTPConnectionCallBacks = (_CFNetConnectionCallBacks*)CFAllocatorAllocate(kCFAllocatorDefault, sizeof(_kFTPConnectionCallBacks[0]), 0);
assert(_kFTPConnectionCallBacks != NULL);
_kFTPConnectionCallBacks->version = 0;
_kFTPConnectionCallBacks->create = (const void* (*)(CFAllocatorRef, const void*))_CFFTPNetConnectionContextCreate;
_kFTPConnectionCallBacks->finalize = (void (*)(CFAllocatorRef, const void*))_CFFTPNetConnectionContextFinalize;
_kFTPConnectionCallBacks->createConnectionStreams = (CFStreamError (*)(CFAllocatorRef, const void*, CFWriteStreamRef*, CFReadStreamRef*))_FTPConnectionCreateStreams;
_kFTPConnectionCallBacks->requestStateChanged = (void (*)(void*, int, CFStreamError*, _CFNetConnectionRef, const void*))_FTPConnectionRequestStateChanged;
_kFTPConnectionCallBacks->transmitRequest = (void (*)(void*, _CFNetConnectionRef, const void*))_FTPConnectionTransmitRequest;
_kFTPConnectionCallBacks->receiveResponse = (void (*)(void*, _CFNetConnectionRef, const void*))_FTPConnectionReceiveResponse;
_kFTPConnectionCallBacks->responseStreamCallBack = (void (*)(void*, CFReadStreamRef, CFStreamEventType, _CFNetConnectionRef, const void*))_FTPResponseStreamCallBack;
_kFTPConnectionCallBacks->requestStreamCallBack = (void (*)(void*, CFWriteStreamRef, CFStreamEventType, _CFNetConnectionRef, const void*))_FTPRequestStreamCallBack;
_kFTPConnectionCallBacks->runLoopAndModesArrayForRequest = (CFArrayRef (*)(void *, _CFNetConnectionRef, const void*))_FTPRunLoopArrayCallBack;
}
gFTPConnectionTimeouts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
gFTPConnectionCache = createConnectionCache();
}
void
_FTPConnectionCacheExpiration(_CFNetConnectionRef conn, CFDateRef expiration, CFMutableArrayRef list) {
if (CFAbsoluteTimeGetCurrent() >= CFDateGetAbsoluteTime(expiration))
CFArrayAppendValue(list, conn);
}
void
_SetSOCKS4ProxyInformation(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt, CFURLRef proxyUrl) {
CFStringRef pHost = CFURLCopyHostName(proxyUrl);
SInt32 p = CFURLGetPortNumber(proxyUrl);
CFNumberRef pPort = CFNumberCreate(alloc, kCFNumberSInt32Type, &p);
CFStringRef pUser = CFURLCopyUserName(proxyUrl);
CFStringRef pPass = CFURLCopyPassword(proxyUrl);
CFMutableDictionaryRef pInfo = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSProxyHost, pHost);
CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSProxyPort, pPort);
CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSVersion, kCFStreamSocketSOCKSVersion4);
if (pUser) {
CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSUser, pUser);
CFRelease(pUser);
}
if (pPass) {
CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSPassword, pPass);
CFRelease(pPass);
}
CFDictionaryAddValue(ctxt->_properties, kCFStreamPropertySOCKSProxy, pInfo);
CFRelease(pInfo);
CFRelease(pHost);
CFRelease(pPort);
}
void
_SetSOCKS5ProxyInformation(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt, CFURLRef proxyUrl) {
CFStringRef pHost = CFURLCopyHostName(proxyUrl);
SInt32 p = CFURLGetPortNumber(proxyUrl);
CFNumberRef pPort = CFNumberCreate(alloc, kCFNumberSInt32Type, &p);
CFStringRef pUser = CFURLCopyUserName(proxyUrl);
CFStringRef pPass = CFURLCopyPassword(proxyUrl);
CFMutableDictionaryRef pInfo = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSProxyHost, pHost);
CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSProxyPort, pPort);
if (pUser) {
CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSUser, pUser);
CFRelease(pUser);
}
if (pPass) {
CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSPassword, pPass);
CFRelease(pPass);
}
CFDictionaryAddValue(ctxt->_properties, kCFStreamPropertySOCKSProxy, pInfo);
CFRelease(pInfo);
CFRelease(pHost);
CFRelease(pPort);
}
void
_StartHTTPRequest(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt, CFStreamError* error, CFURLRef proxyUrl) {
CFURLRef url;
CFHTTPMessageRef msg;
CFMutableDictionaryRef pInfo;
CFURLComponentsRFC1808 comps;
CFStreamClientContext streamCtxt = {0, ctxt, NULL, NULL, NULL};
CFStringRef user = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPUserName);
CFStringRef pass = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPPassword);
CFHTTPMessageRef resp = (CFHTTPMessageRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyFTPLastHTTPResponse);
__CFBitSet(ctxt->_flags, kFlagBitIsHTTPRequest);
memset(error, 0, sizeof(error[0]));
if (user)
CFRetain(user);
else
user = CFURLCopyUserName(ctxt->_url);
if (pass)
CFRetain(pass);
else
pass = CFURLCopyPassword(ctxt->_url);
memset(&comps, 0, sizeof(comps));
_CFURLCopyComponents(ctxt->_url, kCFURLComponentDecompositionRFC1808, &comps);
if (!comps.user)
comps.user = user ? CFURLCreateStringByAddingPercentEscapes(alloc, user, NULL, NULL, kCFStringEncodingUTF8) : NULL;
if (!comps.password)
comps.password = pass ? CFURLCreateStringByAddingPercentEscapes(alloc, pass, NULL, NULL, kCFStringEncodingUTF8) : NULL;
if (comps.query) {
CFRelease(comps.query);
comps.query = NULL;
}
if (comps.fragment) {
CFRelease(comps.fragment);
comps.fragment = NULL;
}
if (comps.parameterString) {
CFRelease(comps.parameterString);
comps.parameterString = NULL;
}
if (user)
CFRelease(user);
if (pass)
CFRelease(pass);
url = _CFURLCreateFromComponents(alloc, kCFURLComponentDecompositionRFC1808, &comps);
if (comps.scheme)
CFRelease(comps.scheme);
if (comps.user)
CFRelease(comps.user);
if (comps.password)
CFRelease(comps.password);
if (comps.host)
CFRelease(comps.host);
if (comps.pathComponents)
CFRelease(comps.pathComponents);
if (comps.baseURL)
CFRelease(comps.baseURL);
msg = CFHTTPMessageCreateRequest(alloc, kHTTPGETMethod, url ? url : ctxt->_url, kCFHTTPVersion1_1);
if (url)
CFRelease(url);
if (resp) {
CFHTTPMessageAddAuthentication(msg,
resp,
CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPProxyUser),
CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPProxyPassword),
NULL,
TRUE);
}
ctxt->_dataStream = CFReadStreamCreateForHTTPRequest(alloc, msg);
CFRelease(msg);
if (!ctxt->_dataStream) {
error->error = errno;
error->domain = kCFStreamErrorDomainPOSIX;
#if defined(__WIN32__)
if (!error->error) {
error->error = WSAGetLastError();
if (error->error)
error->domain = kCFStreamErrorDomainWinSock;
}
#endif
if (!error->error)
error->error = ENOMEM;
}
else {
CFStringRef pScheme = CFURLCopyScheme(proxyUrl);
CFStringRef pHost = CFURLCopyHostName(proxyUrl);
SInt32 p = CFURLGetPortNumber(proxyUrl);
CFNumberRef pPort = CFNumberCreate(alloc, kCFNumberSInt32Type, &p);
CFBooleanRef persistent = (CFBooleanRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPAttemptPersistentConnection);
pInfo = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (CFStringCompare(pScheme, kHTTPSchemeString, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
CFDictionarySetValue(pInfo, kCFStreamPropertyHTTPProxyHost, pHost);
CFDictionarySetValue(pInfo, kCFStreamPropertyHTTPProxyPort, pPort);
}
else {
CFDictionarySetValue(pInfo, kCFStreamPropertyHTTPSProxyHost, pHost);
CFDictionarySetValue(pInfo, kCFStreamPropertyHTTPSProxyPort, pPort);
}
CFReadStreamSetProperty((CFReadStreamRef)ctxt->_dataStream, kCFStreamPropertyHTTPProxy, pInfo);
CFRelease(pInfo);
if (!persistent || CFEqual(persistent, kCFBooleanTrue))
CFReadStreamSetProperty((CFReadStreamRef)ctxt->_dataStream, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue);
CFDictionaryApplyFunction(ctxt->_properties, (CFDictionaryApplierFunction)_StreamPropertyApplier, (void*)ctxt->_dataStream);
CFReadStreamSetClient((CFReadStreamRef)ctxt->_dataStream, ~0L, (CFReadStreamClientCallBack)_DataStreamCallBack, &streamCtxt);
_CFTypeScheduleOnMultipleRunLoops(ctxt->_dataStream,ctxt->_runloops);
CFReadStreamOpen((CFReadStreamRef)ctxt->_dataStream);
CFRelease(pHost);
CFRelease(pPort);
CFRelease(pScheme);
}
}
Boolean
_ProcessHTTPResponse(_CFFTPStreamContext* ctxt, CFStreamError* error) {
UInt32 code;
CFHTTPMessageRef resp = (CFHTTPMessageRef)CFReadStreamCopyProperty((CFReadStreamRef)ctxt->_dataStream, kCFStreamPropertyHTTPResponseHeader);
memset(error, 0, sizeof(error[0]));
if (!resp)
return FALSE;
code = CFHTTPMessageGetResponseStatusCode(resp);
if (code < 300)
__CFBitSet(ctxt->_flags, kFlagBitReadHTTPResponse);
else if (code == 407 && !__CFBitIsSet(ctxt->_flags, kFlagBit407TriedOnce) &&
CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPProxyUser) &&
CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPProxyPassword))
{
Boolean openComplete = FALSE;
__CFBitSet(ctxt->_flags, kFlagBit407TriedOnce);
_ReleaseDataReadStream(ctxt);
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertyFTPLastHTTPResponse, resp);
_FTPStreamOpen(ctxt->_userStream, error, &openComplete, ctxt);
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertyFTPLastHTTPResponse);
}
else {
error->domain = kCFStreamErrorDomainFTP;
error->error = code;
}
CFRelease(resp);
return TRUE;
}
void
_RollOverHTTPRequest(_CFFTPStreamContext* ctxt, CFStreamError* error) {
Boolean openComplete = FALSE;
_ReleaseDataReadStream(ctxt);
ctxt->_current++;
ctxt->_error = *error;
__CFBitClear(ctxt->_flags, kFlagBit407TriedOnce);
_FTPStreamOpen(ctxt->_userStream, error, &openComplete, ctxt);
}
void
_CFStreamSocketCreatedCallBack(int fd, void* ctxt) {
int yes = 1;
(void)ctxt;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(yes));
}
void
_DataStreamCallBack(CFTypeRef stream, CFStreamEventType type, _CFFTPStreamContext* ctxt) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitIsHTTPRequest) || type != kCFStreamEventEndEncountered) {
CFStreamError error;
CFTypeID i = CFReadStreamGetTypeID();
if (CFGetTypeID(stream) == i)
error = CFReadStreamGetError((CFReadStreamRef)stream);
else
error = CFWriteStreamGetError((CFWriteStreamRef)stream);
if (__CFBitIsSet(ctxt->_flags, kFlagBitIsHTTPRequest) && !__CFBitIsSet(ctxt->_flags, kFlagBitReadHTTPResponse))
{
if (type == kCFStreamEventHasBytesAvailable) {
if (_ProcessHTTPResponse(ctxt, &error)) {
if (error.error)
type = kCFStreamEventErrorOccurred;
else if (__CFBitIsSet(ctxt->_flags, kFlagBit407TriedOnce))
return; }
else if (ctxt->_current < CFArrayGetCount(ctxt->_proxies)) {
_RollOverHTTPRequest(ctxt, &error);
if (error.error)
type = kCFStreamEventErrorOccurred;
else
return; }
}
else if ((type == kCFStreamEventErrorOccurred) && (ctxt->_current < CFArrayGetCount(ctxt->_proxies))) {
_RollOverHTTPRequest(ctxt, &error);
if (error.error)
type = kCFStreamEventErrorOccurred;
else
return; }
}
if (CFGetTypeID(ctxt->_userStream) == i)
CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, type, &error);
else
CFWriteStreamSignalEvent((CFWriteStreamRef)ctxt->_userStream, type, &error);
}
}
void
_ReleaseDataReadStream(_CFFTPStreamContext* ctxt) {
CFArrayRef a = ctxt->_runloops;
CFReadStreamRef s = (CFReadStreamRef)ctxt->_dataStream;
CFReadStreamSetClient(s, 0, NULL, NULL);
_CFTypeUnscheduleFromMultipleRunLoops(s, a);
CFReadStreamClose(s);
CFRelease(s);
ctxt->_dataStream = NULL;
}
void
_SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address,
const void *data, _CFFTPStreamContext* ctxt)
{
CFStreamClientContext streamCtxt = {0, ctxt, NULL, NULL, NULL};
CFAllocatorRef alloc = CFGetAllocator(ctxt->_properties);
if (!data || (*((CFSocketNativeHandle*)data) == -1)) {
CFStreamError error = {_kCFStreamErrorDomainNativeSockets, *((int*)data)};
_ReportError(ctxt, &error);
}
if (type != kCFSocketAcceptCallBack)
return;
if (__CFBitIsSet(ctxt->_flags, kFlagBitPerformUpload))
_CFSocketStreamCreatePair(alloc, NULL, 0, *((CFSocketNativeHandle*)data), NULL, NULL, (CFWriteStreamRef*)&ctxt->_dataStream);
else
_CFSocketStreamCreatePair(alloc, NULL, 0, *((CFSocketNativeHandle*)data), NULL, (CFReadStreamRef*)&ctxt->_dataStream, NULL);
CFDictionaryApplyFunction(ctxt->_properties, (CFDictionaryApplierFunction)_StreamPropertyApplier, (void*)ctxt->_dataStream);
if (CFGetTypeID(ctxt->_dataStream) == CFReadStreamGetTypeID()) {
CFReadStreamSetClient((CFReadStreamRef)ctxt->_dataStream, ~0L, (CFReadStreamClientCallBack)_DataStreamCallBack, &streamCtxt);
_CFTypeScheduleOnMultipleRunLoops(ctxt->_dataStream, ctxt->_runloops);
CFReadStreamOpen((CFReadStreamRef)ctxt->_dataStream);
}
else {
CFWriteStreamSetClient((CFWriteStreamRef)ctxt->_dataStream, ~0L, (CFWriteStreamClientCallBack)_DataStreamCallBack, &streamCtxt);
_CFTypeScheduleOnMultipleRunLoops(ctxt->_dataStream, ctxt->_runloops);
CFWriteStreamOpen((CFWriteStreamRef)ctxt->_dataStream);
}
_InvalidateServer(ctxt);
}
void
_StreamPropertyApplier(CFTypeRef key, CFTypeRef value, CFTypeRef stream) {
if (CFGetTypeID(stream) == CFReadStreamGetTypeID())
CFReadStreamSetProperty((CFReadStreamRef)stream, key, value);
else
CFWriteStreamSetProperty((CFWriteStreamRef)stream, key, value);
}
void
_ReportError(_CFFTPStreamContext* ctxt, CFStreamError* error) {
if (ctxt->_connection &&
(((_CFFTPNetConnectionContext*)_CFNetConnectionGetInfoPointer(ctxt->_connection))->_state == kFTPStateConnect) &&
(ctxt->_current < CFArrayGetCount(ctxt->_proxies)))
{
ctxt->_current++;
ctxt->_error = *error;
_CFNetConnectionErrorOccurred(ctxt->_connection, error);
return;
}
__CFBitSet(ctxt->_flags, kFlagBitGotError);
if (ctxt->_connection)
_CFNetConnectionErrorOccurred(ctxt->_connection, error);
if (ctxt->_dataStream) {
if (CFGetTypeID(ctxt->_dataStream) == CFReadStreamGetTypeID())
_ReleaseDataReadStream(ctxt);
else {
_CFTypeInvalidate(ctxt->_dataStream);
_CFTypeUnscheduleFromMultipleRunLoops(ctxt->_dataStream, ctxt->_runloops);
CFWriteStreamClose((CFWriteStreamRef)(ctxt->_dataStream));
CFRelease(ctxt->_dataStream);
ctxt->_dataStream = NULL;
}
}
if (CFGetTypeID(ctxt->_userStream) == CFReadStreamGetTypeID())
CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, error);
else
CFWriteStreamSignalEvent((CFWriteStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, error);
}
void
_ConnectionComplete(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
CFArrayRef a = ftpCtxt->_runloops;
int i, count = CFArrayGetCount(a);
for (i = 0; i < count; i += 2) {
_CFNetConnectionUnschedule(ftpCtxt->_connection,
ftpCtxt,
(CFRunLoopRef)CFArrayGetValueAtIndex(a, i),
(CFStringRef)CFArrayGetValueAtIndex(a, i + 1));
}
ctxt->_state = kFTPStateIdle;
_CFNetConnectionRequestIsComplete(ftpCtxt->_connection, ftpCtxt);
_CFNetConnectionResponseIsComplete(ftpCtxt->_connection, ftpCtxt);
if (!ftpCtxt->_server && !ftpCtxt->_dataStream) {
if (CFGetTypeID(ftpCtxt->_userStream) == CFReadStreamGetTypeID())
CFReadStreamSignalEvent((CFReadStreamRef)ftpCtxt->_userStream, kCFStreamEventEndEncountered, NULL);
else
CFWriteStreamSignalEvent((CFWriteStreamRef)ftpCtxt->_userStream, kCFStreamEventEndEncountered, NULL);
}
}
Boolean
_PASVAddressParser(const UInt8* buffer, struct sockaddr_in* saddr)
{
const UInt8* walk = buffer + 3;
int byteCount = 0;
u_long host = 0;
u_short port = 0;
memset(saddr, 0, sizeof(saddr[0]));
while (*walk) {
if (isdigit(*walk)) {
unsigned temp;
sscanf((const char*)walk, "%ud", &temp);
if (byteCount < 4) {
host |= (temp << (24 - (8 * byteCount)));
} else {
port |= (temp << (8 - (8 * (byteCount - 4))));
}
if (byteCount < 5) {
byteCount ++;
} else {
host = ntohl(host);
memmove(&saddr->sin_addr, &host, sizeof(u_long));
#if !defined(__WIN32__)
saddr->sin_len = sizeof(saddr[0]);
#endif
saddr->sin_family = AF_INET;
saddr->sin_port = ntohs(port);
return TRUE;
}
while (isdigit(*walk))
walk++;
}
walk ++;
}
return FALSE;
}
Boolean
_EPSVPortParser(const UInt8* buffer, struct sockaddr_in6* saddr)
{
const UInt8* walk = buffer + 3;
while (*walk) {
if (!isdigit(*walk)) walk++;
else {
unsigned tmp;
sscanf((const char*)walk, "%ud", &tmp);
saddr->sin6_port = htons((tmp & 0x0000FFFF));
return TRUE;
}
}
return FALSE;
}
u_char
_GetProtocolFamily(_CFFTPStreamContext* ctxt, UInt8* buffer)
{
CFDataRef native = NULL;
socklen_t addrlen = SOCK_MAXADDRLEN;
struct sockaddr* addr = (struct sockaddr*)&(buffer[0]);
u_char result = 255;
CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(ctxt->_connection);
native = CFWriteStreamCopyProperty(wStream, kCFStreamPropertySocketNativeHandle);
if (native) {
memset(buffer, 0, addrlen);
if (!getpeername(*((int*)CFDataGetBytePtr(native)), addr, &addrlen))
result = addr->sa_family;
CFRelease(native);
}
return result;
}
Boolean
_CreateListenerForContext(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt) {
CFDataRef native = NULL;
CFDataRef address = NULL;
CFRunLoopSourceRef src = NULL;
do {
int yes = 1;
UInt8 buffer[SOCK_MAXADDRLEN];
socklen_t addrlen = sizeof(buffer);
struct sockaddr* addr = (struct sockaddr*)&(buffer[0]);
struct sockaddr_in* addr4 = (struct sockaddr_in*)&(buffer[0]);
struct sockaddr_in6* addr6 = (struct sockaddr_in6*)&(buffer[0]);
CFSocketContext socketCtxt = {0, ctxt, NULL, NULL, NULL};
CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(ctxt->_connection);
native = CFWriteStreamCopyProperty(wStream, kCFStreamPropertySocketNativeHandle);
if (!native)
break;
memset(buffer, 0, sizeof(buffer));
if (getsockname(*((int*)CFDataGetBytePtr(native)), addr, &addrlen))
break;
CFRelease(native);
native = NULL;
ctxt->_server = CFSocketCreate(alloc,
addr->sa_family,
SOCK_STREAM,
IPPROTO_TCP,
kCFSocketAcceptCallBack,
(CFSocketCallBack)&_SocketCallBack,
&socketCtxt);
if (!ctxt->_server)
break;
setsockopt(CFSocketGetNative(ctxt->_server), SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes));
if (addr->sa_family == AF_INET)
addr4->sin_port = 0;
else
addr6->sin6_port = 0;
address = CFDataCreateWithBytesNoCopy(alloc, (const UInt8*)addr, (addr->sa_family == AF_INET) ? sizeof(addr4[0]) : sizeof(addr6[0]), kCFAllocatorNull);
if (!address)
break;
if (CFSocketSetAddress(ctxt->_server, address) != kCFSocketSuccess)
break;
CFRelease(address);
address = NULL;
if (!ctxt->_runloops)
return TRUE;
src = CFSocketCreateRunLoopSource(alloc, ctxt->_server, 0);
if (!src)
break;
_CFTypeScheduleOnMultipleRunLoops(src, ctxt->_runloops);
CFRelease(src);
return TRUE;
} while (0);
if (native)
CFRelease(native);
if (address)
CFRelease(address);
if (src)
CFRelease(src);
_InvalidateServer(ctxt);
return FALSE;
}
void
_StartTransfer(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
CFStringRef cmd, target = CFURLCopyLastPathComponent(ftpCtxt->_url);
CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformUpload)) {
ctxt->_state = kFTPStateSTOR;
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPSTORCommandString, target);
if (ftpCtxt->_dataStream)
CFWriteStreamOpen((CFWriteStreamRef)ftpCtxt->_dataStream);
}
else {
if (CFURLHasDirectoryPath(ftpCtxt->_url) || _IsRoot(ftpCtxt->_url)) {
if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformNLST)) {
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPNLSTCommandString, target);
ctxt->_state = kFTPStateNLST;
if (ftpCtxt->_dataStream)
CFReadStreamOpen((CFReadStreamRef)ftpCtxt->_dataStream);
}
else {
cmd = CFRetain(kCFFTPLISTCommandString);
ctxt->_state = kFTPStateLIST;
if (ftpCtxt->_dataStream)
CFReadStreamOpen((CFReadStreamRef)ftpCtxt->_dataStream);
}
}
else {
if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformSTAT)) {
ctxt->_state = kFTPStateSIZE;
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPSIZECommandString, target);
}
else {
if (!ftpCtxt->_offset) {
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRETRCommandString, target);
ctxt->_state = kFTPStateRETR;
if (ftpCtxt->_dataStream)
CFReadStreamOpen((CFReadStreamRef)ftpCtxt->_dataStream);
}
else {
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRESTCommandString, ftpCtxt->_offset);
ctxt->_state = kFTPStateREST;
}
}
}
}
if (target) CFRelease(target);
_WriteCommand(ctxt, ftpCtxt, cmd);
CFRelease(cmd);
}
void
_InvalidateServer(_CFFTPStreamContext* ctxt) {
if (!ctxt->_server)
return;
if (ctxt->_runloops) {
_CFTypeUnscheduleFromMultipleRunLoops(ctxt->_server, ctxt->_runloops);
}
CFSocketInvalidate(ctxt->_server);
CFRelease(ctxt->_server);
ctxt->_server = NULL;
}
CFStringRef
_CreatePathForContext(CFAllocatorRef alloc, _CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
CFStringRef path = CFURLCopyFileSystemPath(ftpCtxt->_url, kCFURLPOSIXPathStyle);
if (!CFStringGetLength(path)) {
CFRelease(path);
path = CFRetain(kCFFTPRootPathString);
}
if (CFStringHasPrefix(path, kCFFTPForcedRootPathPrefix)) {
CFStringRef temp = CFStringCreateWithSubstring(alloc, path, CFRangeMake(1, CFStringGetLength(path) - 1));
if (temp) {
CFRelease(path);
path = temp;
}
}
else if (ctxt->_root) {
CFStringRef temp = CFStringCreateWithFormat(alloc, NULL, kCFFTPPathFormatString, ctxt->_root, path);
if (temp) {
CFRelease(path);
path = temp;
}
}
return path;
}
void
_WriteCommand(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, CFStringRef cmd) {
UInt8* buffer;
CFIndex dlen = CFDataGetLength(ctxt->_sendBuffer);
CFIndex slen = CFStringGetLength(cmd);
CFIndex req = CFStringGetBytes(cmd, CFRangeMake(0, slen), kCFStringEncodingMacRoman, '_', FALSE, NULL, 0, NULL);
CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(ftpCtxt->_connection);
if ((dlen - ctxt->_sendCount) < req)
CFDataSetLength(ctxt->_sendBuffer, dlen + req - (dlen - ctxt->_sendCount));
buffer = CFDataGetMutableBytePtr(ctxt->_sendBuffer);
CFStringGetBytes(cmd, CFRangeMake(0, slen), kCFStringEncodingMacRoman, '_', FALSE, buffer + ctxt->_sendCount, req, NULL);
ctxt->_sendCount += req;
if (ctxt->_sendCount && CFWriteStreamCanAcceptBytes(wStream)) {
_FTPRequestStreamCallBack(ftpCtxt,
wStream,
kCFStreamEventCanAcceptBytes,
ftpCtxt->_connection,
ctxt);
}
}
void
_HandleResponse(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
CFIndex count = ctxt->_recvCount;
while (count >= 4) {
UInt32 result = -1;
UInt8* buffer = (UInt8*)CFDataGetMutableBytePtr(ctxt->_recvBuffer);
Boolean isMultiline = FALSE;
UInt8* newline = memchr(buffer, '\n', count);
if (!newline) break;
if (isdigit(buffer[0]) && isdigit(buffer[1]) && isdigit(buffer[2])) {
result = ((buffer[0] - '0') * 100) + ((buffer[1] - '0') * 10) + (buffer[2] - '0');
if (buffer[3] == '-')
isMultiline = TRUE;
else if (buffer[3] != ' ')
result = -1;
}
if (__CFBitIsSet(ctxt->_flags, kFlagBitMultiline)) {
if ((result == ctxt->_result) && !isMultiline)
__CFBitClear(ctxt->_flags, kFlagBitMultiline);
}
else {
if (*(newline - 1) != '\r') break;
ctxt->_result = result;
if (!(ctxt->_result > 99) && (ctxt->_result < 600)) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
break;
}
if (isMultiline)
__CFBitSet(ctxt->_flags, kFlagBitMultiline);
else
__CFBitClear(ctxt->_flags, kFlagBitMultiline);
}
_AdvanceStateMachine(ctxt, ftpCtxt, buffer, newline - buffer, __CFBitIsSet(ctxt->_flags, kFlagBitMultiline));
newline++;
count -= (newline - buffer);
memmove(buffer, newline, count);
ctxt->_recvCount = count;
}
}
CFURLRef
_ConvertToCFFTPHappyURL(CFURLRef url) {
CFURLRef result = NULL;
UInt8 stack_buffer[2048];
UInt8* buffer = &stack_buffer[0];
CFURLRef tmp = CFURLCopyAbsoluteURL(url);
CFIndex length;
if (!tmp)
return NULL;
url = tmp;
length = CFURLGetBytes(url, buffer, sizeof(stack_buffer));
if (-1 == length) {
CFIndex req = CFURLGetBytes(url, NULL, 0);
buffer = (UInt8*)malloc(req);
if (!buffer) {
CFRelease(tmp);
return NULL;
}
length = CFURLGetBytes(url, buffer, req);
}
result = CFURLCreateAbsoluteURLWithBytes(CFGetAllocator(url), buffer, length, kCFStringEncodingMacRoman, NULL, FALSE);
if (buffer != &stack_buffer[0])
free(buffer);
CFRelease(tmp);
return result;
}
Boolean
_ReadModeBits(const UInt8* str, int* mode) {
Boolean result = TRUE;
int i;
for (i = 0; result && (i < 9); i += 3) {
if (str[i] == 'r') *mode |= (1 << (8 - i)); else if (str[i] != '-') result = FALSE;
if (str[i + 1] == 'w') *mode |= (1 << (7 - i)); else if (str[i + 1] != '-') result = FALSE;
switch (str[i + 2]) {
case 'x':
*mode |= (1 << (6 - i));
break;
case 's':
*mode |= (1 << (6 - i));
if ((i / 3) == 0)
*mode |= 04000;
else if ((i / 3) == 1)
*mode |= 02000;
break;
case 't':
*mode |= (1 << (6 - i));
if ((i / 3) == 2)
*mode |= 01000;
break;
case '-':
break;
case 'S':
if ((i / 3) == 0)
*mode |= 04000;
else if ((i / 3) == 1)
*mode |= 02000;
break;
case 'T':
if ((i / 3) == 2)
*mode |= 01000;
break;
default:
result = FALSE;
break; }
}
return result;
}
Boolean
_ReadSize(const UInt8* str, UInt64* size) {
const UInt8* iter = str;
*size = 0;
if (!isdigit(*iter))
return FALSE;
while (isdigit(*iter))
iter++;
if (!isspace(*iter))
return FALSE;
#if defined(__WIN32__)
*size = _atoi64(str);
#else
*size = strtouq((const char*)str, NULL, 10);
#endif
return TRUE;
}
CFStringRef
_CFStringCreateCopyWithStrippedHTML(CFAllocatorRef alloc, CFStringRef theString) {
CFStringRef result = NULL;
if (!CFStringHasPrefix(theString, kHTMLTagOpen) || !CFStringHasSuffix(theString, kHTMLTagClose))
result = CFStringCreateCopy(alloc, theString);
else {
CFRange r1 = CFStringFind(theString, kHTMLTagClose, 0);
CFRange r2 = CFStringFind(theString, kHTMLTagOpen, kCFCompareBackwards);
if (r1.location >= r2.location)
result = CFStringCreateCopy(alloc, theString);
else {
r1.length = (r2.location - r1.location) - 1;
r1.location += 1;
result = CFStringCreateWithSubstring(alloc, theString, r1);
}
}
return result;
}
const UInt8*
_CFFTPGetDateTimeFunc(CFAllocatorRef alloc, const UInt8* str, CFIndex length, CFDateRef* date) {
static const char kMonthStrs[12][3] = {
{'J', 'a', 'n'},
{'F', 'e', 'b'},
{'M', 'a', 'r'},
{'A', 'p', 'r'},
{'M', 'a', 'y'},
{'J', 'u', 'n'},
{'J', 'u', 'l'},
{'A', 'u', 'g'},
{'S', 'e', 'p'},
{'O', 'c', 't'},
{'N', 'o', 'v'},
{'D', 'e', 'c'}
};
CFIndex i;
Boolean hourIsYear = TRUE;
SInt8 month, day, minute = 0;
SInt32 hour = 0;
*date = NULL;
if (length < 9)
return NULL;
for (month = 0; month < (sizeof(kMonthStrs) / sizeof(kMonthStrs[0])); month++) {
if (!memcmp(str, kMonthStrs[month], 3)) {
break;
}
}
if ((month == (sizeof(kMonthStrs) / sizeof(kMonthStrs[0]))) || !isspace(str[3]))
return NULL;
month++;
i = 4;
while ((i < length) && !isdigit(str[i]))
i++;
if (i == length)
return NULL;
day = (str[i++] - '0');
if (i == length)
return NULL;
if (isdigit(str[i])) {
day *= 10;
day += (str[i++] - '0');
}
if ((i == length) || !isspace(str[i]))
return NULL;
while ((i < length) && !isdigit(str[i]))
i++;
while ((i < length) && isdigit(str[i])) {
hour *= 10;
hour += (str[i++] - '0');
}
if ((i < length) && (str[i] == ':')) {
hourIsYear = FALSE;
i++;
while ((i < length) && isdigit(str[i])) {
minute *= 10;
minute += (str[i++] - '0');
}
}
if ((i == length) || isspace(str[i])) {
CFTimeZoneRef tz = CFTimeZoneCopyDefault();
CFAbsoluteTime t = CFAbsoluteTimeGetCurrent();
CFGregorianDate d = CFAbsoluteTimeGetGregorianDate(t, tz);
if (hourIsYear) {
if (hour < 100)
d.year = 1900 + hour;
else
d.year = hour;
d.hour = 0;
d.minute = 0;
d.second = 0;
d.month = month;
d.day = day;
}
else {
CFAbsoluteTime t2;
CFGregorianDate d2 = CFAbsoluteTimeGetGregorianDate((t + 86400.0), tz);
d.hour = hour & 0xFF;
d.minute = minute;
d.second = 0;
d.month = month;
d.day = day;
d.year = d2.year;
t2 = CFGregorianDateGetAbsoluteTime(d, tz);
if (t2 > (t + 86400.0))
d.year--;
}
t = CFGregorianDateGetAbsoluteTime(d, tz);
if (tz)
CFRelease(tz);
*date = CFDateCreate(alloc, t);
return &str[i];
}
return NULL;
}
void
_ProxyStreamCallBack(CFReadStreamRef proxyStream, _CFFTPStreamContext* ctxt) {
Boolean complete = FALSE;
ctxt->_proxies = _CFNetworkCopyProxyFromProxyStream(ctxt->_proxyStream, &complete);
if (complete) {
CFStreamError error = CFReadStreamGetError(ctxt->_proxyStream);
_CFTypeUnscheduleFromMultipleRunLoops(ctxt->_proxyStream, ctxt->_runloops);
CFRelease(ctxt->_proxyStream);
ctxt->_proxyStream = NULL;
if (ctxt->_proxies) {
Boolean open;
_FTPStreamOpen(ctxt->_userStream, &error, &open, ctxt);
if (open) {
CFStreamEventType event = kCFStreamEventErrorOccurred;
if (!error.error)
event = kCFStreamEventOpenCompleted;
if (CFGetTypeID(ctxt->_userStream) == CFReadStreamGetTypeID())
CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, event, &error);
else
CFWriteStreamSignalEvent((CFWriteStreamRef)ctxt->_userStream, event, &error);
}
}
else {
if (!error.error) {
error.domain = kCFStreamErrorDomainPOSIX;
error.error = errno;
if (!error.error)
error.error = EIO;
}
_ReportError(ctxt, &error);
}
}
}
#if defined(__WIN32__)
extern void
_CFFTPCleanup(void) {
__CFSpinLock(&gFTPSpinLock);
if (gFTPConnectionCache != NULL) {
releaseConnectionCache(gFTPConnectionCache);
gFTPConnectionCache = NULL;
}
__CFSpinUnlock(&gFTPSpinLock);
}
#endif
CFIndex
_FindLine(const UInt8 *buffer, CFIndex bufferLength, const UInt8** start, const UInt8** end) {
CFIndex consumed;
*start = NULL;
*end = NULL;
consumed = 0;
if ( (buffer != NULL) && (bufferLength != 0) ) {
const UInt8* lastBufChar;
const UInt8* startOfLine;
lastBufChar = buffer + bufferLength - 1;
startOfLine = buffer;
while ( startOfLine <= lastBufChar ) {
if ( *startOfLine != '\r' && *startOfLine != '\n' ) {
break;
}
++startOfLine;
}
if ( startOfLine <= lastBufChar ) {
const UInt8* endOfLine;
const UInt8* firstend = NULL;
endOfLine = startOfLine;
while ( endOfLine <= lastBufChar ) {
if ( *endOfLine == '\r' || *endOfLine == '\n' ) {
break;
}
++endOfLine;
}
firstend = endOfLine;
if ( endOfLine <= lastBufChar ) {
const UInt8* lastend;
*start = startOfLine;
*end = endOfLine;
lastend = firstend;
while ( lastend <= lastBufChar ) {
if ( *lastend != '\r' && *lastend != '\n' ) {
break;
}
++lastend;
}
consumed = lastend - buffer;
}
else {
consumed = startOfLine - buffer;
}
}
else {
consumed = bufferLength;
}
}
return ( consumed );
}
#if 0
#pragma mark -
#pragma mark State Machine
#endif
void
_AdvanceStateMachine(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length, Boolean isMultiLine) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
if (ctxt->_state <= kFTPStateIdle)
__CFBitClear(ctxt->_flags, kFlagBitReturnToIdle);
else if (ctxt->_state < kFTPStateRETR) {
if (!isMultiLine) {
ctxt->_state = kFTPStateIdle;
_StartProcess(ctxt, ftpCtxt);
}
return;
}
}
switch (ctxt->_state) {
case kFTPStateConnect:
if (!isMultiLine) _HandleConnect(ctxt, ftpCtxt, line, length);
break;
case kFTPStateUSER:
if (!isMultiLine) _HandleUsername(ctxt, ftpCtxt);
break;
case kFTPStatePASS:
if (!isMultiLine) _HandlePassword(ctxt, ftpCtxt);
break;
case kFTPStateSYST:
if (!isMultiLine) _HandleSystem(ctxt, ftpCtxt, line, length);
break;
case kFTPStateSITEDIRSTYLE:
if (!isMultiLine) _HandleSiteDirStyle(ctxt, ftpCtxt, line, length);
break;
case kFTPStateSITETRUTH:
if (!isMultiLine) _HandleSiteTruth(ctxt, ftpCtxt);
break;
case kFTPStatePWD:
if (!isMultiLine) _HandlePrintWorkingDirectory(ctxt, ftpCtxt, line, length);
break;
case kFTPStateTYPE:
if (!isMultiLine) _HandleType(ctxt, ftpCtxt);
break;
case kFTPStatePASV:
if (!isMultiLine) _HandlePassive(ctxt, ftpCtxt, line, length);
break;
case kFTPStatePORT:
if (!isMultiLine) _HandlePort(ctxt, ftpCtxt);
break;
case kFTPStateSIZE:
if (!isMultiLine) _HandleSize(ctxt, ftpCtxt, line, length);
break;
case kFTPStateSTAT:
_HandleStat(ctxt, ftpCtxt, line, length, isMultiLine);
break;
case kFTPStateREST:
if (!isMultiLine) _HandleRestart(ctxt, ftpCtxt);
break;
case kFTPStateRETR:
if (!isMultiLine) _HandleRetrieve(ctxt, ftpCtxt);
break;
case kFTPStateNLST:
if (!isMultiLine) _HandleNameList(ctxt, ftpCtxt);
break;
case kFTPStateCWD:
if (!isMultiLine) _HandleChangeDirectory(ctxt, ftpCtxt);
break;
case kFTPStateLIST:
if (!isMultiLine) _HandleList(ctxt, ftpCtxt);
break;
case kFTPStateSTOR:
if (!isMultiLine) _HandleStore(ctxt, ftpCtxt);
break;
case kFTPStateMKD:
if (!isMultiLine) _HandleMakeDirectory(ctxt, ftpCtxt);
break;
case kFTPStateRMD:
if (!isMultiLine) _HandleRemoveDirectory(ctxt, ftpCtxt);
break;
case kFTPStateDELE:
if (!isMultiLine) _HandleDelete(ctxt, ftpCtxt);
break;
case kFTPStateRNFR:
if (!isMultiLine) _HandleRenameFrom(ctxt, ftpCtxt);
break;
case kFTPStateRNTO:
if (!isMultiLine) _HandleRenameTo(ctxt, ftpCtxt);
break;
default:
break;
}
}
void
_HandleConnect(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length) {
if (ctxt->_result < 200)
return;
else if (ctxt->_result >= 300) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else {
CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_runloops);
CFStringRef system = CFStringCreateWithBytes(alloc,
line,
length,
kCFStringEncodingUTF8,
FALSE);
if (!system) {
system = CFStringCreateWithBytes(alloc,
line,
length,
kCFStringEncodingISOLatin1,
FALSE);
}
if (system) {
CFRange range = CFRangeMake(0, CFStringGetLength(system));
if (CFStringFindWithOptions(system, kCFFTPOSXSystemString, range, 0, NULL))
__CFBitSet(ctxt->_flags, kFlagBitIsXServer);
CFRelease(system);
}
ctxt->_state = kFTPStateUSER;
}
}
void
_HandleUsername(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if ((ctxt->_result < 200) || (ctxt->_result >= 400)) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else if (ctxt->_result >= 300) {
CFStringRef cmd;
CFStringRef pass = CFDictionaryGetValue(ftpCtxt->_properties, kCFStreamPropertyFTPPassword);
if (pass)
CFRetain(pass);
else {
pass = CFURLCopyPassword(ftpCtxt->_url);
if (!pass)
pass = CFRetain(kAnonymousPasswordString);
}
cmd = CFStringCreateWithFormat(CFGetAllocator(ftpCtxt->_runloops), NULL, kCFFTPPASSCommandString, pass);
CFRelease(pass);
if (cmd) {
ctxt->_state = kFTPStatePASS;
_WriteCommand(ctxt, ftpCtxt, cmd);
CFRelease(cmd);
}
else {
CFStreamError error = {kCFStreamErrorDomainPOSIX, errno};
if (!error.error)
error.error = ENOMEM;
_ReportError(ftpCtxt, &error);
}
}
else {
ctxt->_state = kFTPStateSYST;
_WriteCommand(ctxt, ftpCtxt, kCFFTPSYSTCommandString);
}
}
void
_HandlePassword(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if ((ctxt->_result < 200) || (ctxt->_result >= 400)) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else if (ctxt->_result >= 300) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else {
ctxt->_state = kFTPStateSYST;
_WriteCommand(ctxt, ftpCtxt, kCFFTPSYSTCommandString);
}
}
void
_HandleSystem(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length) {
CFStringRef system = NULL;
CFRange range;
if ((ctxt->_result >= 200) && (ctxt->_result < 300)) {
system = CFStringCreateWithBytes(CFGetAllocator(ftpCtxt->_properties),
line,
length,
kCFStringEncodingUTF8,
FALSE);
range = CFRangeMake(0, CFStringGetLength(system));
}
if (system && CFStringFindWithOptions(system, kCFFTPWindowsNTSystemString, range, 0, NULL)) {
ctxt->_state = kFTPStateSITEDIRSTYLE;
_WriteCommand(ctxt, ftpCtxt, kCFFTPSITEDIRSTYLECommandString);
}
else if( __CFBitIsSet(ctxt->_flags, kFlagBitIsXServer)) {
ctxt->_state = kFTPStateSITETRUTH;
_WriteCommand(ctxt, ftpCtxt, kCFFTPSITETRUTHCommandString);
} else {
ctxt->_state = kFTPStatePWD;
_WriteCommand(ctxt, ftpCtxt, kCFFTPPWDCommandString);
}
if (system)
CFRelease(system);
}
void
_HandleSiteDirStyle(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length) {
if ((ctxt->_result >= 200) && (ctxt->_result < 300)) {
CFStringRef system = CFStringCreateWithBytes(CFGetAllocator(ftpCtxt->_properties),
line,
length,
kCFStringEncodingUTF8,
FALSE);
CFRange range = CFRangeMake(0, CFStringGetLength(system));
if (CFStringFindWithOptions(system, kCFFTPMSDOSSystemString, range, 0, NULL)) {
ctxt->_state = kFTPStateSITEDIRSTYLE;
_WriteCommand(ctxt, ftpCtxt, kCFFTPSITEDIRSTYLECommandString);
CFRelease(system);
return;
}
CFRelease(system);
}
if( __CFBitIsSet(ctxt->_flags, kFlagBitIsXServer)) {
ctxt->_state = kFTPStateSITETRUTH;
_WriteCommand(ctxt, ftpCtxt, kCFFTPSITETRUTHCommandString);
} else {
ctxt->_state = kFTPStatePWD;
_WriteCommand(ctxt, ftpCtxt, kCFFTPPWDCommandString);
}
}
void
_HandleSiteTruth(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
ctxt->_state = kFTPStatePWD;
_WriteCommand(ctxt, ftpCtxt, kCFFTPPWDCommandString);
}
void
_HandlePrintWorkingDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length) {
if ((ctxt->_result >= 200) && (ctxt->_result < 300)) {
const UInt8* first = memchr(line, '"', length);
if (first) {
const UInt8* last = line + length - 1;
first++;
while ( last != first) {
if (*last == '"') {
if (*(last - 1) == '/')
last--;
ctxt->_root = CFStringCreateWithBytes(CFGetAllocator(ftpCtxt->_properties),
first,
last - first,
kCFStringEncodingUTF8,
FALSE);
break;
}
last--;
}
}
}
ctxt->_state = kFTPStateTYPE;
_WriteCommand(ctxt, ftpCtxt, kCFFTPTYPECommandString);
}
void
_HandleType(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if ((ctxt->_result < 200) || (ctxt->_result >= 300)) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else {
ctxt->_state = kFTPStateIdle;
_StartProcess(ctxt, ftpCtxt);
}
}
void
_HandleChangeDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitLeftForDead) && (ctxt->_result == 421)) {
CFStreamError error;
Boolean openComplete;
_CFNetConnectionLost(ftpCtxt->_connection);
_CFNetConnectionDequeue(ftpCtxt->_connection, ftpCtxt);
_FTPStreamOpen(ftpCtxt->_userStream, &error, &openComplete, ftpCtxt);
}
else if ((ctxt->_result >= 300) || (ctxt->_result < 200)){
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
__CFBitClear(ctxt->_flags, kFlagBitLeftForDead);
_ReportError(ftpCtxt, &error);
}
else {
CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
__CFBitClear(ctxt->_flags, kFlagBitLeftForDead);
if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformUpload)) {
CFStringRef target = CFURLCopyLastPathComponent(ftpCtxt->_url);
if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitRemoveResource)) {
CFStringRef cmd;
if (CFURLHasDirectoryPath(ftpCtxt->_url) || _IsRoot(ftpCtxt->_url)) {
ctxt->_state = kFTPStateRMD;
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRMDCommandString, target);
}
else {
ctxt->_state = kFTPStateDELE;
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPDELECommandString, target);
}
_WriteCommand(ctxt, ftpCtxt, cmd);
CFRelease(cmd);
CFWriteStreamSignalEvent((CFWriteStreamRef)ftpCtxt->_userStream, kCFStreamEventOpenCompleted, NULL);
}
else if (ftpCtxt->_newUrl) {
CFStringRef cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRNFRCommandString, target);
ctxt->_state = kFTPStateRNFR;
_WriteCommand(ctxt, ftpCtxt, cmd);
CFRelease(cmd);
CFWriteStreamSignalEvent((CFWriteStreamRef)ftpCtxt->_userStream, kCFStreamEventOpenCompleted, NULL);
}
else if (CFURLHasDirectoryPath(ftpCtxt->_url) || _IsRoot(ftpCtxt->_url)) {
CFStringRef cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPMKDCommandString, target);
ctxt->_state = kFTPStateMKD;
_WriteCommand(ctxt, ftpCtxt, cmd);
CFRelease(cmd);
CFWriteStreamSignalEvent((CFWriteStreamRef)ftpCtxt->_userStream, kCFStreamEventOpenCompleted, NULL);
}
if (target) CFRelease(target);
}
if (ctxt->_state == kFTPStateCWD) {
if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformPASV)) {
UInt8 buf[SOCK_MAXADDRLEN];
u_char family = _GetProtocolFamily(ftpCtxt, buf);
ctxt->_state = kFTPStatePASV;
if (family == AF_INET)
_WriteCommand(ctxt, ftpCtxt, kCFFTPPASVCommandString);
else if (family == AF_INET6)
_WriteCommand(ctxt, ftpCtxt, kCFFTPEPSVCommandString);
else {
CFStreamError error = {kCFStreamErrorDomainFTP, 522}; _ReportError(ftpCtxt, &error);
}
}
else if (_CreateListenerForContext(alloc, ftpCtxt)) {
CFDataRef addr = CFSocketCopyAddress(ftpCtxt->_server);
CFStringRef cmd = NULL;
struct sockaddr_in* sa = (struct sockaddr_in*)CFDataGetBytePtr(addr);
struct sockaddr_in6* sa6 = (struct sockaddr_in6*)sa;
if (sa->sin_family == AF_INET) { cmd = CFStringCreateWithFormat(alloc,
NULL,
kCFFTPPORTCommandString,
(unsigned int)((UInt8*)(&(sa->sin_addr)))[0],
(unsigned int)((UInt8*)(&(sa->sin_addr)))[1],
(unsigned int)((UInt8*)(&(sa->sin_addr)))[2],
(unsigned int)((UInt8*)(&(sa->sin_addr)))[3],
(unsigned int)((UInt8*)(&(sa->sin_port)))[0],
(unsigned int)((UInt8*)(&(sa->sin_port)))[1]);
} else if (sa->sin_family == AF_INET6) { cmd = CFStringCreateWithFormat(alloc,
NULL,
kCFFTPEPRTCommandString,
(unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[0]),
(unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[1]),
(unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[2]),
(unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[3]),
(unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[4]),
(unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[5]),
(unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[6]),
(unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[7]),
(unsigned int)ntohs(((UInt16*)(&(sa6->sin6_port)))[0]));
} else { CFStreamError error = {kCFStreamErrorDomainFTP, 522}; _ReportError(ftpCtxt, &error);
}
if (cmd) {
ctxt->_state = kFTPStatePORT;
_WriteCommand(ctxt, ftpCtxt, cmd);
CFRelease(cmd);
}
}
else {
CFStreamError error = {kCFStreamErrorDomainPOSIX, errno};
#if defined(__WIN32__)
if (!error.error) {
error.error = WSAGetLastError();
if (error.error)
error.domain = kCFStreamErrorDomainWinSock;
}
#endif
if (!error.error) {
error.error = ENOTCONN;
error.domain = _kCFStreamErrorDomainNativeSockets;
}
_ReportError(ftpCtxt, &error);
}
}
}
}
void
_HandlePassive(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length) {
if ((ctxt->_result < 200) || (ctxt->_result >= 300)) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else {
UInt8 buf[SOCK_MAXADDRLEN];
u_char family = _GetProtocolFamily(ftpCtxt, buf);
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
if (family == AF_INET6)
memcpy(&addr6, buf, sizeof(struct sockaddr_in6));
if ((family == AF_INET && !_PASVAddressParser(line, &addr4)) ||
(family == AF_INET6 && !_EPSVPortParser(line, &addr6)))
{
CFStreamError error = {_kCFStreamErrorDomainNativeSockets, EADDRNOTAVAIL};
_ReportError(ftpCtxt, &error);
}
else {
CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
CFStreamClientContext streamCtxt = {0, ftpCtxt, NULL, NULL, NULL};
CFSocketSignature sig;
sig.protocolFamily = family;
sig.socketType = SOCK_STREAM;
sig.protocol = IPPROTO_TCP;
if (family == AF_INET)
sig.address = CFDataCreate(alloc, (const UInt8*)&addr4, sizeof(addr4));
else
sig.address = CFDataCreate(alloc, (const UInt8*)&addr6, sizeof(addr6));
if (!sig.address) {
CFStreamError error = {kCFStreamErrorDomainPOSIX, ENOMEM};
_ReportError(ftpCtxt, &error);
return;
}
if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformUpload))
_CFSocketStreamCreatePair(alloc, NULL, 0, 0, &sig, NULL, (CFWriteStreamRef*)&ftpCtxt->_dataStream);
else
_CFSocketStreamCreatePair(alloc, NULL, 0, 0, &sig, (CFReadStreamRef*)&ftpCtxt->_dataStream, NULL);
CFRelease(sig.address);
if (!ftpCtxt->_dataStream) {
CFStreamError error = {kCFStreamErrorDomainPOSIX, ENOMEM};
_ReportError(ftpCtxt, &error);
return;
}
if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformUpload))
CFWriteStreamSetClient((CFWriteStreamRef)ftpCtxt->_dataStream, ~0L, (CFWriteStreamClientCallBack)_DataStreamCallBack, &streamCtxt);
else
CFReadStreamSetClient((CFReadStreamRef)ftpCtxt->_dataStream, ~0L, (CFReadStreamClientCallBack)_DataStreamCallBack, &streamCtxt);
_CFTypeScheduleOnMultipleRunLoops(ftpCtxt->_dataStream, ftpCtxt->_runloops);
CFDictionaryApplyFunction(ftpCtxt->_properties, (CFDictionaryApplierFunction)_StreamPropertyApplier, (void*)ftpCtxt->_dataStream);
_StartTransfer(ctxt, ftpCtxt);
}
}
}
void
_HandlePort(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if ((ctxt->_result < 200) || (ctxt->_result >= 300)) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else
_StartTransfer(ctxt, ftpCtxt);
}
void
_HandleStat(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length, Boolean isMultiLine) {
if (!ftpCtxt->_attributes) {
CFFTPCreateParsedResourceListing(CFGetAllocator(ftpCtxt->_properties),
line,
length,
&ftpCtxt->_attributes);
}
if (isMultiLine)
return;
if (isdigit(line[0])) {
CFStringRef cmd;
CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
if (!ftpCtxt->_offset) {
CFStringRef path = _CreatePathForContext(alloc, ctxt, ftpCtxt);
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRETRCommandString, path);
ctxt->_state = kFTPStateRETR;
CFRelease(path);
if (ftpCtxt->_dataStream)
CFReadStreamOpen((CFReadStreamRef)ftpCtxt->_dataStream);
}
else {
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRESTCommandString, ftpCtxt->_offset);
ctxt->_state = kFTPStateREST;
}
_WriteCommand(ctxt, ftpCtxt, cmd);
CFRelease(cmd);
}
}
void
_HandleSize(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length) {
CFStringRef cmd;
CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
if ((ctxt->_result >= 500) && (ctxt->_result < 600)) {
CFStringRef target = CFURLCopyLastPathComponent(ftpCtxt->_url);
if (ftpCtxt->_attributes) {
CFRelease(ftpCtxt->_attributes);
ftpCtxt->_attributes = NULL;
}
ctxt->_state = kFTPStateSTAT;
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPSTATCommandString, target);
if (target) CFRelease(target);
}
else {
if (ctxt->_result == 213) {
CFIndex i = 0;
while ((i < length) && isdigit(line[i]))
i++;
while ((i < length) && !isdigit(line[i]))
i++;
if (i < length) {
UInt8* end = NULL;
#if defined(__WIN32__)
long long s = _atoi64(&line[i]);
#else
long long s = strtouq((const char*)&line[i], (char**)&end, 0);
#endif
if (!(((s == ULLONG_MAX) && (errno)) || ((s == 0) && (end == &line[i])))) {
const void *keys[1], *values[1];
keys[0] = kCFFTPResourceSize;
values[0] = CFNumberCreate(alloc, kCFNumberLongLongType, &s);
if (values[0]) {
ftpCtxt->_attributes = CFDictionaryCreate(alloc, keys, values, 1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFRelease(values[0]);
}
}
}
}
if (!ftpCtxt->_offset) {
CFStringRef path = _CreatePathForContext(alloc, ctxt, ftpCtxt);
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRETRCommandString, path);
ctxt->_state = kFTPStateRETR;
CFRelease(path);
if (ftpCtxt->_dataStream)
CFReadStreamOpen((CFReadStreamRef)ftpCtxt->_dataStream);
}
else {
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRESTCommandString, ftpCtxt->_offset);
ctxt->_state = kFTPStateREST;
}
}
_WriteCommand(ctxt, ftpCtxt, cmd);
CFRelease(cmd);
}
void
_HandleRestart(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if ((ctxt->_result < 300) || (ctxt->_result >= 400)) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else {
CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
CFStringRef cmd, path = _CreatePathForContext(alloc, ctxt, ftpCtxt);
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRETRCommandString, path);
CFRelease(path);
ctxt->_state = kFTPStateRETR;
if (ftpCtxt->_dataStream)
CFReadStreamOpen((CFReadStreamRef)ftpCtxt->_dataStream);
_WriteCommand(ctxt, ftpCtxt, cmd);
CFRelease(cmd);
}
}
void
_HandleRetrieve(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if (ctxt->_result < 200)
return;
if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
ctxt->_state = kFTPStateIdle;
_StartProcess(ctxt, ftpCtxt);
}
else {
if (ctxt->_result >= 300) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else if (ctxt->_result >= 200) {
_ConnectionComplete(ctxt, ftpCtxt);
}
}
}
void
_HandleNameList(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if (ctxt->_result < 200)
return;
if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
ctxt->_state = kFTPStateIdle;
_StartProcess(ctxt, ftpCtxt);
}
else {
if (ctxt->_result >= 300) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else {
_ConnectionComplete(ctxt, ftpCtxt);
}
}
}
void
_HandleList(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if (ctxt->_result < 200)
return;
if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
ctxt->_state = kFTPStateIdle;
_StartProcess(ctxt, ftpCtxt);
}
else {
if (ctxt->_result >= 300) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else if (ctxt->_result < 300) {
_ConnectionComplete(ctxt, ftpCtxt);
}
}
}
void
_HandleStore(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if (ctxt->_result < 200)
return;
if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
ctxt->_state = kFTPStateIdle;
_StartProcess(ctxt, ftpCtxt);
}
else {
if (ctxt->_result >= 300) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else if (ctxt->_result < 300) {
_ConnectionComplete(ctxt, ftpCtxt);
}
}
}
void
_HandleMakeDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
ctxt->_state = kFTPStateIdle;
_StartProcess(ctxt, ftpCtxt);
}
else {
if ((ctxt->_result >= 300) || (ctxt->_result < 200)) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else {
_ConnectionComplete(ctxt, ftpCtxt);
}
}
}
void
_HandleRemoveDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
ctxt->_state = kFTPStateIdle;
_StartProcess(ctxt, ftpCtxt);
}
else {
if ((ctxt->_result >= 300) || (ctxt->_result < 200)) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else {
_ConnectionComplete(ctxt, ftpCtxt);
}
}
}
void
_HandleDelete(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
ctxt->_state = kFTPStateIdle;
_StartProcess(ctxt, ftpCtxt);
}
else {
if ((ctxt->_result >= 300) || (ctxt->_result < 200)) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else {
_ConnectionComplete(ctxt, ftpCtxt);
}
}
}
void
_HandleRenameFrom(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if ((ctxt->_result < 300) || (ctxt->_result >= 400)) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else {
CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
CFStringRef cmd, path;
CFURLRef url = ftpCtxt->_url;
ftpCtxt->_url = ftpCtxt->_newUrl;
path = _CreatePathForContext(alloc, ctxt, ftpCtxt);
ftpCtxt->_url = url;
ctxt->_state = kFTPStateRNTO;
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRNTOCommandString, path);
CFRelease(path);
_WriteCommand(ctxt, ftpCtxt, cmd);
CFRelease(cmd);
}
}
void
_HandleRenameTo(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
ctxt->_state = kFTPStateIdle;
_StartProcess(ctxt, ftpCtxt);
}
else {
if ((ctxt->_result >= 300) || (ctxt->_result < 200)) {
CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
_ReportError(ftpCtxt, &error);
}
else {
_ConnectionComplete(ctxt, ftpCtxt);
}
}
}
void
_StartProcess(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitLogInOnly)) {
if (CFGetTypeID(ftpCtxt->_userStream) == CFReadStreamGetTypeID())
CFReadStreamSignalEvent((CFReadStreamRef)ftpCtxt->_userStream, kCFStreamEventOpenCompleted, NULL);
else
CFWriteStreamSignalEvent((CFWriteStreamRef)ftpCtxt->_userStream, kCFStreamEventOpenCompleted, NULL);
_ConnectionComplete(ctxt, ftpCtxt);
}
else {
CFStringRef path, cmd;
CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
__CFBitClear(ctxt->_flags, kFlagBitReturnToIdle);
if (!__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformUpload) &&
(CFURLHasDirectoryPath(ftpCtxt->_url) || _IsRoot(ftpCtxt->_url)))
{
path = _CreatePathForContext(alloc, ctxt, ftpCtxt);
}
else {
CFURLRef old = ftpCtxt->_url;
ftpCtxt->_url = CFURLCreateCopyDeletingLastPathComponent(alloc, old);
path = _CreatePathForContext(alloc, ctxt, ftpCtxt);
CFRelease(ftpCtxt->_url);
ftpCtxt->_url = old;
}
cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPCWDCommandString, path);
CFRelease(path);
ctxt->_state = kFTPStateCWD;
_WriteCommand(ctxt, ftpCtxt, cmd);
CFRelease(cmd);
}
}
#if 0
#pragma mark -
#pragma mark Extern Function Definitions (API)
#endif
CFWriteStreamRef
CFWriteStreamCreateWithFTPURL(CFAllocatorRef alloc, CFURLRef ftpURL) {
CFWriteStreamRef result = NULL;
CFStringRef temp;
_CFFTPStreamContext* ctxt;
if (!ftpURL || !(ftpURL = _ConvertToCFFTPHappyURL(ftpURL)))
return result;
temp = CFURLCopyScheme(ftpURL);
if (!temp) {
CFRelease(ftpURL);
return result;
}
if ((CFStringCompare(temp, kFTPSchemeString, 0) != kCFCompareEqualTo) &&
(CFStringCompare(temp, kFTPSSchemeString, 0) != kCFCompareEqualTo))
{
CFRelease(ftpURL);
CFRelease(temp);
return result;
}
CFRelease(temp);
temp = CFURLCopyHostName(ftpURL);
if (!temp) {
CFRelease(ftpURL);
return result;
}
CFRelease(temp);
ctxt = (_CFFTPStreamContext*)CFAllocatorAllocate(alloc,
sizeof(ctxt[0]),
0);
if (ctxt) {
memset(ctxt, 0, sizeof(ctxt[0]));
__CFBitSet(ctxt->_flags, kFlagBitPerformPASV);
__CFBitSet(ctxt->_flags, kFlagBitPerformUpload);
ctxt->_url = CFURLCopyAbsoluteURL(ftpURL);
ctxt->_runloops = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
ctxt->_properties = CFDictionaryCreateMutable(alloc,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (ctxt->_url && ctxt->_runloops && ctxt->_properties) {
CFWriteStreamCallBacks _FTPWriteStreamCallBacks;
memset(&_FTPWriteStreamCallBacks, 0, sizeof(_FTPWriteStreamCallBacks));
_FTPWriteStreamCallBacks.version = 1;
_FTPWriteStreamCallBacks.finalize = (void (*)(CFWriteStreamRef, void*))_FTPStreamFinalize;
_FTPWriteStreamCallBacks.copyDescription = (CFStringRef (*)(CFWriteStreamRef, void*))_FTPStreamCopyDescription;
_FTPWriteStreamCallBacks.open = (Boolean (*)(CFWriteStreamRef, CFStreamError*, Boolean*, void*))_FTPStreamOpen;
_FTPWriteStreamCallBacks.openCompleted = (Boolean (*)(CFWriteStreamRef, CFStreamError*, void*))_FTPStreamOpenCompleted;
_FTPWriteStreamCallBacks.write = (CFIndex (*)(CFWriteStreamRef, const UInt8*, CFIndex, CFStreamError*, void*))_FTPStreamWrite;
_FTPWriteStreamCallBacks.canWrite = (Boolean (*)(CFWriteStreamRef, void*))_FTPStreamCanWrite;
_FTPWriteStreamCallBacks.close = (void (*)(CFWriteStreamRef, void*))_FTPStreamClose;
_FTPWriteStreamCallBacks.copyProperty = (CFTypeRef (*)(CFWriteStreamRef, CFStringRef, void*))_FTPStreamCopyProperty;
_FTPWriteStreamCallBacks.setProperty = (Boolean (*)(CFWriteStreamRef, CFStringRef, CFTypeRef, void*))_FTPStreamSetProperty;
_FTPWriteStreamCallBacks.schedule = (void (*)(CFWriteStreamRef, CFRunLoopRef, CFStringRef, void*))_FTPStreamSchedule;
_FTPWriteStreamCallBacks.unschedule = (void (*)(CFWriteStreamRef, CFRunLoopRef, CFStringRef, void*))_FTPStreamUnschedule;
result = CFWriteStreamCreate(alloc, &_FTPWriteStreamCallBacks, ctxt);
}
if (result) {
ctxt->_userStream = result;
temp = CFURLCopyUserName(ftpURL);
if (temp) {
CFWriteStreamSetProperty(result, kCFStreamPropertyFTPUserName, temp);
CFRelease(temp);
}
temp = CFURLCopyPassword(ftpURL);
if (temp) {
CFWriteStreamSetProperty(result, kCFStreamPropertyFTPPassword, temp);
CFRelease(temp);
}
}
else {
if (ctxt->_url)
CFRelease(ctxt->_url);
if (ctxt->_runloops)
CFRelease(ctxt->_runloops);
if (ctxt->_properties)
CFRelease(ctxt->_properties);
CFAllocatorDeallocate(alloc, ctxt);
}
}
CFRelease(ftpURL);
return result;
}
CFReadStreamRef
CFReadStreamCreateWithFTPURL(CFAllocatorRef alloc, CFURLRef ftpURL) {
CFReadStreamRef result = NULL;
CFStringRef temp;
_CFFTPStreamContext* ctxt;
if (!ftpURL || !(ftpURL = _ConvertToCFFTPHappyURL(ftpURL)))
return result;
temp = CFURLCopyScheme(ftpURL);
if (!temp) {
CFRelease(ftpURL);
return result;
}
if ((CFStringCompare(temp, kFTPSchemeString, 0) != kCFCompareEqualTo) &&
(CFStringCompare(temp, kFTPSSchemeString, 0) != kCFCompareEqualTo))
{
CFRelease(ftpURL);
CFRelease(temp);
return result;
}
CFRelease(temp);
temp = CFURLCopyHostName(ftpURL);
if (!temp) {
CFRelease(ftpURL);
return result;
}
CFRelease(temp);
ctxt = (_CFFTPStreamContext*)CFAllocatorAllocate(alloc,
sizeof(ctxt[0]),
0);
if (ctxt) {
memset(ctxt, 0, sizeof(ctxt[0]));
__CFBitSet(ctxt->_flags, kFlagBitPerformPASV);
ctxt->_url = CFURLCopyAbsoluteURL(ftpURL);
ctxt->_runloops = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
ctxt->_properties = CFDictionaryCreateMutable(alloc,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (ctxt->_url && ctxt->_runloops && ctxt->_properties) {
CFReadStreamCallBacks _FTPReadStreamCallBacks;
memset(&_FTPReadStreamCallBacks, 0, sizeof(_FTPReadStreamCallBacks));
_FTPReadStreamCallBacks.version = 1;
_FTPReadStreamCallBacks.finalize = (void (*)(CFReadStreamRef, void*))_FTPStreamFinalize;
_FTPReadStreamCallBacks.copyDescription = (CFStringRef (*)(CFReadStreamRef, void*))_FTPStreamCopyDescription;
_FTPReadStreamCallBacks.open = (Boolean (*)(CFReadStreamRef, CFStreamError*, Boolean*, void*))_FTPStreamOpen;
_FTPReadStreamCallBacks.openCompleted = (Boolean (*)(CFReadStreamRef, CFStreamError*, void*))_FTPStreamOpenCompleted;
_FTPReadStreamCallBacks.read = (CFIndex (*)(CFReadStreamRef, UInt8*, CFIndex, CFStreamError*, Boolean*, void*))_FTPStreamRead;
_FTPReadStreamCallBacks.canRead = (Boolean (*)(CFReadStreamRef, void*))_FTPStreamCanRead;
_FTPReadStreamCallBacks.close = (void (*)(CFReadStreamRef, void*))_FTPStreamClose;
_FTPReadStreamCallBacks.copyProperty = (CFTypeRef (*)(CFReadStreamRef, CFStringRef, void*))_FTPStreamCopyProperty;
_FTPReadStreamCallBacks.setProperty = (Boolean (*)(CFReadStreamRef, CFStringRef, CFTypeRef, void*))_FTPStreamSetProperty;
_FTPReadStreamCallBacks.schedule = (void (*)(CFReadStreamRef, CFRunLoopRef, CFStringRef, void*))_FTPStreamSchedule;
_FTPReadStreamCallBacks.unschedule = (void (*)(CFReadStreamRef, CFRunLoopRef, CFStringRef, void*))_FTPStreamUnschedule;
result = CFReadStreamCreate(alloc, &_FTPReadStreamCallBacks, ctxt);
}
if (result) {
ctxt->_userStream = result;
temp = CFURLCopyUserName(ftpURL);
if (temp) {
CFReadStreamSetProperty(result, kCFStreamPropertyFTPUserName, temp);
CFRelease(temp);
}
temp = CFURLCopyPassword(ftpURL);
if (temp) {
CFReadStreamSetProperty(result, kCFStreamPropertyFTPPassword, temp);
CFRelease(temp);
}
}
else {
if (ctxt->_url)
CFRelease(ctxt->_url);
if (ctxt->_runloops)
CFRelease(ctxt->_runloops);
if (ctxt->_properties)
CFRelease(ctxt->_properties);
CFAllocatorDeallocate(alloc, ctxt);
}
}
CFRelease(ftpURL);
return result;
}
CFIndex
CFFTPCreateParsedResourceListing(CFAllocatorRef alloc, const UInt8 *buffer,
CFIndex bufferLength, CFDictionaryRef *parsed)
{
CFIndex totalConsumed;
*parsed = NULL;
totalConsumed = 0;
if ( (buffer != NULL) && (bufferLength != 0) ) {
const UInt8* scanStart; CFIndex scanLength;
scanStart = buffer;
scanLength = bufferLength;
do {
CFIndex consumed; const UInt8* first; const UInt8* eol;
consumed = _FindLine(scanStart, scanLength, &first, &eol);
totalConsumed += consumed;
scanStart += consumed;
scanLength -= consumed;
if ( first == NULL ) {
break;
}
if (memcmp("total ", first, 6)) {
int count = 0;
const UInt8* fields[16];
memset(fields, 0, sizeof(fields));
while ((count < (sizeof(fields) / sizeof(fields[0]))) && (first < eol)) {
while ((first < eol) && isspace(*first))
first++;
if (first >= eol)
break;
fields[count++] = first;
while ((first < eol) && !isspace(*first))
first++;
}
if (count) {
int type, mode;
Boolean hadModeBits = TRUE;
Boolean foundSize = FALSE;
switch (fields[0][0]) {
case 'b': type = DT_BLK; break; case 'c': type = DT_CHR; break; case 'd': type = DT_DIR; break; case 'l': type = DT_LNK; break; case 's': type = DT_SOCK; break; case 'p': type = DT_FIFO; break; case '-': type = DT_REG; break; default: type = DT_UNKNOWN; break;
}
mode = 0;
if ((eol - fields[0]) < 11)
hadModeBits = FALSE;
else
hadModeBits = _ReadModeBits(&fields[0][1], &mode);
if (fields[3] && fields[4]) {
int i = 3;
UInt64 size = 0;
const UInt8* user = NULL;
const UInt8* group = NULL;
CFDateRef date = NULL;
while (fields[i]) {
const UInt8* end = _CFFTPGetDateTimeFunc(alloc, fields[i], eol - fields[i], &date);
if (date) {
int j = i - 1;
while (j >= 0) {
if (!j && hadModeBits)
break;
if (_ReadSize(fields[j], &size)) {
foundSize = TRUE;
j--;
if (j || !hadModeBits) {
group = fields[j];
j--;
if (!j && hadModeBits)
user = group;
else {
UInt64 linkcount = 0;
if (hadModeBits && (j == 1) && _ReadSize(fields[j], &linkcount))
user = group;
else
user = fields[j];
}
}
break;
}
}
while (fields[i] && (end > fields[i]))
i++;
break;
}
i++;
}
if (fields[i] && date) {
int j = 0;
const UInt8* tmp = NULL;
const UInt8* name = fields[i];
const UInt8* link = NULL;
const void *keys[kResourceInfoItemCount], *values[kResourceInfoItemCount];
if (type == DT_LNK) {
while (fields[i]) {
if (!memcmp(fields[i++], "->", 2)) {
link = fields[i];
break;
}
}
}
if (hadModeBits) {
keys[j] = kCFFTPResourceMode;
values[j] = CFNumberCreate(alloc, kCFNumberSInt32Type, &mode);
if (values[j]) j++;
}
keys[j] = kCFFTPResourceName;
values[j] = CFStringCreateWithBytes(alloc, name, !link ? eol - name : (fields[i - 1] - 1) - name, kCFStringEncodingMacRoman, FALSE);
if (values[j]) {
CFStringRef temp = _CFStringCreateCopyWithStrippedHTML(alloc, values[j]);
if (temp) {
CFRelease(values[j]);
values[j] = temp;
}
j++;
}
if (link) {
keys[j] = kCFFTPResourceLink;
values[j] = CFStringCreateWithBytes(alloc, link, eol - link, kCFStringEncodingMacRoman, FALSE);
if (values[j]) j++;
}
else {
keys[j] = kCFFTPResourceLink;
values[j] = CFStringCreateWithCString(alloc, "", kCFStringEncodingUTF8);
if (values[j]) j++;
}
if (foundSize) {
if (group && (group == user)) {
const char kUserGroupSeparators[] = {'|', ':', '/', '\\'};
const UInt8* sep = NULL;
const UInt8* end = user;
while (!isspace(*end))
end++;
for (i = 0; !sep && (i < (sizeof(kUserGroupSeparators) / sizeof(kUserGroupSeparators[0]))); i++)
sep = memchr(user, kUserGroupSeparators[i], end - user);
if (sep) {
tmp = sep; group = sep + 1;
}
}
if (user) {
if (!tmp) {
for (tmp = user; !isspace(*tmp); tmp++)
;
}
keys[j] = kCFFTPResourceOwner;
values[j] = CFStringCreateWithBytes(alloc, user, tmp - user, kCFStringEncodingMacRoman, FALSE);
if (values[j]) j++;
}
if (group) {
for (tmp = group; !isspace(*tmp); tmp++)
;
keys[j] = kCFFTPResourceGroup;
values[j] = CFStringCreateWithBytes(alloc, group, tmp - group, kCFStringEncodingMacRoman, FALSE);
if (values[j]) j++;
}
keys[j] = kCFFTPResourceSize;
values[j] = CFNumberCreate(alloc, kCFNumberLongLongType, &size);
if (values[j]) j++;
}
keys[j] = kCFFTPResourceType;
values[j] = CFNumberCreate(alloc, kCFNumberIntType, &type);
if (values[j]) j++;
keys[j] = kCFFTPResourceModDate;
values[j++] = CFRetain(date);
*parsed = CFDictionaryCreate(alloc, keys, values, j, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
for (--j; j >= 0; j--)
CFRelease(values[j]);
break;
}
if (date)
CFRelease(date);
}
if (!hadModeBits) {
const void *keys[1], *values[1];
keys[0] = kCFFTPResourceName;
values[0] = CFStringCreateWithBytes(alloc, fields[0], eol - fields[0], kCFStringEncodingMacRoman, FALSE);
if (values[0]) {
if (!CFStringHasPrefix(values[0], kHTMLTagOpen) || !CFStringHasSuffix(values[0], kHTMLTagClose)) {
*parsed = CFDictionaryCreate(alloc, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease(values[0]);
break;
}
CFRelease(values[0]);
}
}
}
}
if ( totalConsumed >= bufferLength ) {
break;
}
} while (1);
}
return ( totalConsumed );
}