/ src / FTP / CFFTPStream.c
CFFTPStream.c
   1  /*
   2   * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
   3   *
   4   * @APPLE_LICENSE_HEADER_START@
   5   * 
   6   * This file contains Original Code and/or Modifications of Original Code
   7   * as defined in and that are subject to the Apple Public Source License
   8   * Version 2.0 (the 'License'). You may not use this file except in
   9   * compliance with the License. Please obtain a copy of the License at
  10   * http://www.opensource.apple.com/apsl/ and read it before using this
  11   * file.
  12   * 
  13   * The Original Code and all software distributed under the License are
  14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  18   * Please see the License for the specific language governing rights and
  19   * limitations under the License.
  20   * 
  21   * @APPLE_LICENSE_HEADER_END@
  22   */
  23  /* CFFTPStream.c
  24     Copyright 2000, Apple, Inc.  All rights reserved.
  25  */
  26  
  27  #if 0
  28  #pragma mark Includes
  29  #endif
  30  #include <CFNetwork/CFNetwork.h>
  31  #include "CFNetworkInternal.h"			// for _CFNetConnectionCacheKey and friends
  32  
  33  #include "CFStreamPriv.h"
  34  #include "CFNetConnection.h"
  35  #include <CFNetwork/CFFTPStream.h>
  36  #include "CFFTPStreamPriv.h"
  37  #include "CFPriv.h"
  38  #include "CFSocketStreamPriv.h"
  39  #include "CFPriv.h"
  40  #include "CFHTTPConnectionPriv.h"  // for the asynchronous proxy lookup
  41  #include "CFNetworkSchedule.h"
  42  #include <SystemConfiguration/SystemConfiguration.h>
  43  
  44  #ifdef APPORTABLE
  45  
  46  #define     DT_UNKNOWN       0
  47  #define     DT_FIFO          1
  48  #define     DT_CHR           2
  49  #define     DT_DIR           4
  50  #define     DT_BLK           6
  51  #define     DT_REG           8
  52  #define     DT_LNK          10
  53  #define     DT_SOCK         12
  54  #define     DT_WHT          14
  55  #include <sys/param.h>
  56  #endif
  57  
  58  #if 0
  59  #pragma mark *Win32 Specifics
  60  #endif
  61  #if defined(__WIN32__)
  62  #include <sys/param.h>
  63  #include <winsock2.h>
  64  #include <ws2tcpip.h>
  65  #define SOCK_MAXADDRLEN 255
  66  
  67  // Sockets and fds are not interchangeable on Win32, and have different error codes.
  68  // These redefines assumes that in this file we only apply this error constant to socket ops.
  69  #undef ENOTCONN
  70  #define ENOTCONN WSAENOTCONN
  71  
  72  #undef EADDRNOTAVAIL
  73  #define EADDRNOTAVAIL WSAEADDRNOTAVAIL
  74  
  75  #define     DT_UNKNOWN       0
  76  #define     DT_FIFO          1
  77  #define     DT_CHR           2
  78  #define     DT_DIR           4
  79  #define     DT_BLK           6
  80  #define     DT_REG           8
  81  #define     DT_LNK          10
  82  #define     DT_SOCK         12
  83  #define     DT_WHT          14
  84  
  85  #else
  86  
  87  #if 0
  88  #pragma mark *Mach Specifics
  89  #endif
  90  #include <sys/socket.h>
  91  #include <netinet/in.h>
  92  #include <arpa/inet.h>
  93  #include <sys/dirent.h>
  94  #include <netinet/tcp.h>
  95  #endif
  96  
  97  
  98  #if 0
  99  #pragma mark -
 100  #pragma mark Constants
 101  #endif
 102  
 103  /* extern */ const SInt32 kCFStreamErrorDomainFTP = 6;
 104  
 105  
 106  #if 0
 107  #pragma mark -
 108  #pragma mark Constant Strings
 109  #pragma mark *Stream Property Keys
 110  #endif
 111  
 112  // The following properties when set effect the key created for the
 113  // connection cache, so their values should be stored in the "properties"
 114  // dictionary on the ftp stream context.
 115  CONST_STRING_DECL(kCFStreamPropertyFTPUserName, "kCFStreamPropertyFTPUserName")
 116  CONST_STRING_DECL(kCFStreamPropertyFTPUserName_prevalidated, "kCFStreamPropertyFTPUserName_prevalidated")
 117  CONST_STRING_DECL(kCFStreamPropertyFTPPassword, "kCFStreamPropertyFTPPassword")
 118  CONST_STRING_DECL(kCFStreamPropertyFTPPassword_prevalidated, "kCFStreamPropertyFTPPassword_prevalidated")
 119  CONST_STRING_DECL(kCFStreamPropertyFTPProxy, "kCFStreamPropertyFTPProxy")
 120  CONST_STRING_DECL(kCFStreamPropertyFTPAttemptPersistentConnection, "kCFStreamPropertyFTPAttemptPersistentConnection")
 121  
 122  
 123  // The following properties when set should NOT effect the key created
 124  // for the connection cache, therefore their values are stored as bits
 125  // or as ivars on the ftp stream context.
 126  CONST_STRING_DECL(kCFStreamPropertyFTPUsePassiveMode, "kCFStreamPropertyFTPUsePassiveMode")
 127  CONST_STRING_DECL(kCFStreamPropertyFTPFetchResourceInfo, "kCFStreamPropertyFTPFetchResourceInfo")
 128  CONST_STRING_DECL(kCFStreamPropertyFTPFileTransferOffset, "kCFStreamPropertyFTPFileTransferOffset")
 129  CONST_STRING_DECL(_kCFStreamPropertyFTPLogInOnly, "_kCFStreamPropertyFTPLogInOnly")  // SPI for connecting and logging in only
 130  CONST_STRING_DECL(_kCFStreamPropertyFTPRemoveResource, "_kCFStreamPropertyFTPRemoveResource")  // SPI for removing the specified URL
 131  CONST_STRING_DECL(_kCFStreamPropertyFTPNewResourceName, "_kCFStreamPropertyFTPNewResourceName")  // SPI for creating the specified URL
 132  #ifdef __CONSTANT_CFSTRINGS__
 133  #define kCFStreamPropertyFTPFetchNameList	CFSTR("kCFStreamPropertyFTPFetchNameList")
 134  #else
 135  static CONST_STRING_DECL(kCFStreamPropertyFTPFetchNameList, "kCFStreamPropertyFTPFetchNameList")  // SPI property key to perform a NLST instead of a LIST
 136  #endif	/* __CONSTANT_CFSTRINGS__ */
 137  
 138  // The following properties are copy only.
 139  CONST_STRING_DECL(kCFStreamPropertyFTPResourceSize, "kCFStreamPropertyFTPResourceSize")
 140  
 141  // The following property is internal to FTP and is used to cary over
 142  // 407 responses in order to apply authentication to the new request.
 143  #ifdef __CONSTANT_CFSTRINGS__
 144  #define _kCFStreamPropertyFTPLastHTTPResponse	CFSTR("_kCFStreamPropertyFTPLastHTTPResponse")
 145  #else
 146  static CONST_STRING_DECL(_kCFStreamPropertyFTPLastHTTPResponse, "_kCFStreamPropertyFTPLastHTTPResponse")
 147  #endif	/* __CONSTANT_CFSTRINGS__ */
 148  
 149  #if 0
 150  #pragma mark *Various Dictionary Keys
 151  #endif
 152  
 153  // Proxy dictionary keys
 154  CONST_STRING_DECL(kCFStreamPropertyFTPProxyHost, "FTPProxy")
 155  CONST_STRING_DECL(kCFStreamPropertyFTPProxyPort, "FTPPort")
 156  CONST_STRING_DECL(kCFStreamPropertyFTPProxyPassword, "kCFStreamPropertyFTPProxyPassword")
 157  CONST_STRING_DECL(kCFStreamPropertyFTPProxyUser, "kCFStreamPropertyFTPProxyUser")
 158  
 159  
 160  // Resource info dictionary keys.
 161  #define kResourceInfoItemCount 8L
 162  CONST_STRING_DECL(kCFFTPResourceMode, "kCFFTPResourceMode")
 163  CONST_STRING_DECL(kCFFTPResourceName, "kCFFTPResourceName")
 164  CONST_STRING_DECL(kCFFTPResourceOwner, "kCFFTPResourceOwner")
 165  CONST_STRING_DECL(kCFFTPResourceGroup, "kCFFTPResourceGroup")
 166  CONST_STRING_DECL(kCFFTPResourceLink, "kCFFTPResourceLink")
 167  CONST_STRING_DECL(kCFFTPResourceSize, "kCFFTPResourceSize")
 168  CONST_STRING_DECL(kCFFTPResourceType, "kCFFTPResourceType")
 169  CONST_STRING_DECL(kCFFTPResourceModDate, "kCFFTPResourceModDate")
 170  
 171  
 172  #if 0
 173  #pragma mark *Other Strings
 174  #endif
 175  
 176  // Scheme strings used for comparison in order to set up default
 177  // information (e.g. port).
 178  #ifdef __CONSTANT_CFSTRINGS__
 179  #define kFTPSchemeString	CFSTR("ftp")
 180  #define kFTPSSchemeString	CFSTR("ftps")
 181  #define kSOCKS4SchemeString	CFSTR("socks4")
 182  #define kSOCKS5SchemeString	CFSTR("socsk5")
 183  #define kHTTPSchemeString	CFSTR("http")
 184  #define kHTTPSSchemeString	CFSTR("https")
 185  #else
 186  static CONST_STRING_DECL(kFTPSchemeString, "ftp")
 187  static CONST_STRING_DECL(kFTPSSchemeString, "ftps")
 188  static CONST_STRING_DECL(kSOCKS4SchemeString, "socks4")
 189  static CONST_STRING_DECL(kSOCKS5SchemeString, "socsk5")
 190  static CONST_STRING_DECL(kHTTPSchemeString, "http")
 191  static CONST_STRING_DECL(kHTTPSSchemeString, "https")
 192  #endif	/* __CONSTANT_CFSTRINGS__ */
 193  
 194  // Anonymous username and password
 195  #ifdef __CONSTANT_CFSTRINGS__
 196  #define kAnonymousUserString		CFSTR("anonymous")
 197  #define kAnonymousPasswordString	CFSTR("cfnetwork@apple.com")
 198  #else
 199  static CONST_STRING_DECL(kAnonymousUserString, "anonymous")
 200  static CONST_STRING_DECL(kAnonymousPasswordString, "cfnetwork@apple.com")
 201  #endif	/* __CONSTANT_CFSTRINGS__ */
 202  
 203  // Format for producing ftp proxy "username"
 204  #ifdef __CONSTANT_CFSTRINGS__
 205  #define kFTPProxyFormat			CFSTR("%@@%@")
 206  #define kFTPProxyWithPortFormat	CFSTR("%@@%@:%ld")
 207  #else
 208  static CONST_STRING_DECL(kFTPProxyFormat, "%@@%@")
 209  static CONST_STRING_DECL(kFTPProxyWithPortFormat, "%@@%@:%ld")
 210  #endif	/* __CONSTANT_CFSTRINGS__ */
 211  
 212  // Method used when downloading over http
 213  #ifdef __CONSTANT_CFSTRINGS__
 214  #define kHTTPGETMethod	CFSTR("GET")
 215  #else
 216  static CONST_STRING_DECL(kHTTPGETMethod, "GET")
 217  #endif	/* __CONSTANT_CFSTRINGS__ */
 218  
 219  // Used to strip HTML tags from directory listings.
 220  #ifdef __CONSTANT_CFSTRINGS__
 221  #define kHTMLTagOpen	CFSTR("<")
 222  #define kHTMLTagClose	CFSTR(">")
 223  #else
 224  static CONST_STRING_DECL(kHTMLTagOpen, "<")
 225  static CONST_STRING_DECL(kHTMLTagClose, ">")
 226  #endif	/* __CONSTANT_CFSTRINGS__ */
 227  
 228  // Command format strings
 229  #ifdef __CONSTANT_CFSTRINGS__
 230  #define kCFFTPUSERCommandString				CFSTR("USER %@\r\n")
 231  #define kCFFTPPASSCommandString				CFSTR("PASS %@\r\n")
 232  #define kCFFTPSYSTCommandString				CFSTR("SYST\r\n")
 233  #define kCFFTPSITEDIRSTYLECommandString		CFSTR("SITE DIRSTYLE\r\n")
 234  #define kCFFTPSITETRUTHCommandString		CFSTR("SITE TRUTH ON\r\n")
 235  #define kCFFTPPWDCommandString				CFSTR("PWD\r\n")
 236  #define kCFFTPTYPECommandString				CFSTR("TYPE I\r\n")
 237  #define kCFFTPPASVCommandString				CFSTR("PASV\r\n")
 238  #define kCFFTPEPSVCommandString				CFSTR("EPSV\r\n")
 239  #define kCFFTPPORTCommandString				CFSTR("PORT %lu,%lu,%lu,%lu,%lu,%lu\r\n")
 240  #define kCFFTPEPRTCommandString				CFSTR("EPRT |2|%x:%x:%x:%x:%x:%x:%x:%x|%lu|\r\n")
 241  #define kCFFTPRESTCommandString				CFSTR("REST %lld\r\n")
 242  #define kCFFTPSTATCommandString				CFSTR("STAT %@\r\n")
 243  #define kCFFTPSIZECommandString				CFSTR("SIZE %@\r\n")
 244  #define kCFFTPRETRCommandString				CFSTR("RETR %@\r\n")
 245  #define kCFFTPNLSTCommandString				CFSTR("NLST %@\r\n")
 246  #define kCFFTPCWDCommandString				CFSTR("CWD %@\r\n")
 247  #define kCFFTPLISTCommandString				CFSTR("LIST\r\n")
 248  #define kCFFTPSTORCommandString				CFSTR("STOR %@\r\n")
 249  #define kCFFTPMKDCommandString				CFSTR("MKD %@\r\n")
 250  #define kCFFTPRMDCommandString				CFSTR("RMD %@\r\n")
 251  #define kCFFTPDELECommandString				CFSTR("DELE %@\r\n")
 252  #define kCFFTPRNFRCommandString				CFSTR("RNFR %@\r\n")
 253  #define kCFFTPRNTOCommandString				CFSTR("RNTO %@\r\n")
 254  #else
 255  static CONST_STRING_DECL(kCFFTPUSERCommandString, "USER %@\r\n")
 256  static CONST_STRING_DECL(kCFFTPPASSCommandString, "PASS %@\r\n")
 257  static CONST_STRING_DECL(kCFFTPSYSTCommandString, "SYST\r\n")
 258  static CONST_STRING_DECL(kCFFTPSITEDIRSTYLECommandString, "SITE DIRSTYLE\r\n")
 259  static CONST_STRING_DECL(kCFFTPSITETRUTHCommandString, "SITE TRUTH ON\r\n")
 260  static CONST_STRING_DECL(kCFFTPPWDCommandString, "PWD\r\n")
 261  static CONST_STRING_DECL(kCFFTPTYPECommandString, "TYPE I\r\n")
 262  static CONST_STRING_DECL(kCFFTPPASVCommandString, "PASV\r\n")
 263  static CONST_STRING_DECL(kCFFTPEPSVCommandString, "EPSV\r\n")
 264  static CONST_STRING_DECL(kCFFTPPORTCommandString, "PORT %lu,%lu,%lu,%lu,%lu,%lu\r\n")
 265  static CONST_STRING_DECL(kCFFTPEPRTCommandString, "EPRT |2|%x:%x:%x:%x:%x:%x:%x:%x|%lu|\r\n")
 266  static CONST_STRING_DECL(kCFFTPRESTCommandString, "REST %lld\r\n")
 267  static CONST_STRING_DECL(kCFFTPSTATCommandString, "STAT %@\r\n")
 268  static CONST_STRING_DECL(kCFFTPSIZECommandString, "SIZE %@\r\n")
 269  static CONST_STRING_DECL(kCFFTPRETRCommandString, "RETR %@\r\n")
 270  static CONST_STRING_DECL(kCFFTPNLSTCommandString, "NLST %@\r\n")
 271  static CONST_STRING_DECL(kCFFTPCWDCommandString, "CWD %@\r\n")
 272  static CONST_STRING_DECL(kCFFTPLISTCommandString, "LIST\r\n")
 273  static CONST_STRING_DECL(kCFFTPSTORCommandString, "STOR %@\r\n")
 274  static CONST_STRING_DECL(kCFFTPMKDCommandString, "MKD %@\r\n")
 275  static CONST_STRING_DECL(kCFFTPRMDCommandString, "RMD %@\r\n")
 276  static CONST_STRING_DECL(kCFFTPDELECommandString, "DELE %@\r\n")
 277  static CONST_STRING_DECL(kCFFTPRNFRCommandString, "RNFR %@\r\n")
 278  static CONST_STRING_DECL(kCFFTPRNTOCommandString, "RNTO %@\r\n")
 279  #endif	/* __CONSTANT_CFSTRINGS__ */
 280  
 281  // Path format for combining root with url path
 282  #ifdef __CONSTANT_CFSTRINGS__
 283  #define kCFFTPPathFormatString	CFSTR("%@%@")
 284  #else
 285  static CONST_STRING_DECL(kCFFTPPathFormatString, "%@%@")
 286  #endif	/* __CONSTANT_CFSTRINGS__ */
 287  
 288  // Path for when only a host is given
 289  #ifdef __CONSTANT_CFSTRINGS__
 290  #define kCFFTPRootPathString	CFSTR("/")
 291  #else
 292  static CONST_STRING_DECL(kCFFTPRootPathString, "/")
 293  #endif	/* __CONSTANT_CFSTRINGS__ */
 294  
 295  // Path prefix indicating full path (no root)
 296  #ifdef __CONSTANT_CFSTRINGS__
 297  #define kCFFTPForcedRootPathPrefix	CFSTR("//")
 298  #else
 299  static CONST_STRING_DECL(kCFFTPForcedRootPathPrefix, "//");
 300  #endif	/* __CONSTANT_CFSTRINGS__ */
 301  
 302  // Comparison strings for determining DIRSTYLE
 303  #ifdef __CONSTANT_CFSTRINGS__
 304  #define kCFFTPWindowsNTSystemString		CFSTR("Windows_NT")
 305  #define kCFFTPMSDOSSystemString			CFSTR("MSDOS-like directory output is on")
 306  #else
 307  static CONST_STRING_DECL(kCFFTPWindowsNTSystemString, "Windows_NT")
 308  static CONST_STRING_DECL(kCFFTPMSDOSSystemString, "MSDOS-like directory output is on")
 309  #endif	/* __CONSTANT_CFSTRINGS__ */
 310  
 311  // Comparison string for determining TRUTH
 312  #ifdef __CONSTANT_CFSTRINGS__
 313  #define kCFFTPOSXSystemString	CFSTR("Mac OS X Server")
 314  #else
 315  static CONST_STRING_DECL(kCFFTPOSXSystemString, "Mac OS X Server")
 316  #endif	/* __CONSTANT_CFSTRINGS__ */
 317  
 318  // Run loop mode for waiting for stream to open
 319  #ifdef __CONSTANT_CFSTRINGS__
 320  #define kCFFTPStreamOpenCompleted	CFSTR("_FTPStreamOpenCompleted")
 321  #else
 322  static CONST_STRING_DECL(kCFFTPStreamOpenCompleted, "_FTPStreamOpenCompleted")
 323  #endif	/* __CONSTANT_CFSTRINGS__ */
 324  
 325  // Strings used for CopyDescription function
 326  #ifdef __CONSTANT_CFSTRINGS__
 327  #define kCFFTPStreamDescriptionFormat	CFSTR("<FTPStream %p>{%@, url = %@, flags = 0x%x }")
 328  #define kCFFTPStreamUploadDescription	CFSTR("upload")
 329  #define kCFFTPStreamDownloadDescription	CFSTR("download")
 330  #else
 331  #error crap
 332  static CONST_STRING_DECL(kCFFTPStreamDescriptionFormat, "<FTPStream %p>{%@, url = %@, flags = 0x%x }")
 333  static CONST_STRING_DECL(kCFFTPStreamUploadDescription, "upload")
 334  static CONST_STRING_DECL(kCFFTPStreamDownloadDescription, "download")
 335  #endif	/* __CONSTANT_CFSTRINGS__ */
 336  
 337  // It's sad that this is really needed.  Used for escape sequences in URL's.
 338  #ifdef __CONSTANT_CFSTRINGS__
 339  #define kCFFTPStreamEmptyString	CFSTR("")
 340  #else
 341  static CONST_STRING_DECL(kCFFTPStreamEmptyString, "")
 342  #endif	/* __CONSTANT_CFSTRINGS__ */
 343  
 344  
 345  #if 0
 346  #pragma mark -
 347  #pragma mark Enum Values
 348  #endif
 349  
 350  // State machine states.  These really indicate the command
 351  // on which the state machine is waiting for a reply.
 352  // NOTE:  THESE ARE ORDER DEPENDENT!!!
 353  //	See _AdvanceStateMachine and _FTPConnectionRequestStateChanged
 354  typedef enum {
 355      kFTPStateConnect = 0,
 356      kFTPStateUSER,
 357      kFTPStatePASS,
 358      kFTPStateSYST,
 359      kFTPStateSITEDIRSTYLE,
 360      kFTPStateSITETRUTH,
 361      kFTPStatePWD,
 362      kFTPStateTYPE,
 363      kFTPStateIdle,
 364      kFTPStateCWD,
 365      kFTPStatePASV,
 366      kFTPStatePORT,
 367      kFTPStateSTAT,
 368      kFTPStateSIZE,
 369      kFTPStateREST,
 370      kFTPStateRETR,		// The following must be last.
 371      kFTPStateNLST,
 372      kFTPStateLIST,
 373      kFTPStateSTOR,
 374      kFTPStateMKD,
 375      kFTPStateRMD,
 376      kFTPStateDELE,
 377      kFTPStateRNFR,
 378      kFTPStateRNTO
 379  } _CFFTPStreamState;
 380  
 381  enum {
 382      // _CFFTPStreamContext flags
 383      kFlagBitPerformPASV = 0,	// Passive is on by default
 384  	kFlagBitDidSetPassiveBit,	// Set if SetProperty of the passive property
 385      kFlagBitPerformSTAT,	// Get the size of the target object
 386      kFlagBitPerformNLST,	// Perform NLIST instead of LIST command
 387      kFlagBitIsHTTPRequest,	// Indicates that the given request is ftp over http
 388      kFlagBitReadHTTPResponse,	// Already consulted the http response from server
 389      kFlagBit407TriedOnce,	// Already attempted a 407 retry
 390      kFlagBitPerformUpload,	// Indicates a write stream
 391      kFlagBitRemoveResource,	// Used to remove the resource pointed to by the url
 392      kFlagBitLogInOnly,		// Used by csmount to "test" connect
 393      kFlagBitGotError,		// Used to protect when dequeueing as a result of an
 394                         		// error and trying to requeue orphaned items.
 395  	kFlagBitCompleteDeferred,	// During RETR, set by first code executed (no data on the datastream or getting a complete response)
 396  	
 397      // _CFFTPNetConnection flags
 398      kFlagBitMultiline	= 0,	// In the process of a multiline response
 399      kFlagBitReturnToIdle,	// Return back to the idle state before proceeding to next request
 400      kFlagBitIsXServer,		// This connection is to an OS X server
 401      kFlagBitLeftForDead,	// Connection has no pending requests but is still in cache
 402      kFlagBitHTTPLitmus,		// Indicates having sent the early HTTP test
 403      
 404      // Other constants
 405      kBufferGrowthSize	= 2048,	// Growth factor used for reading responses to commands
 406      kFTPTimeoutInSeconds = 180,	// Timeout for stale connections sitting in the connection cache
 407      
 408      // CFNetConnection types for cache key
 409      kCFNetConnectionTypeFTP,
 410      kCFNetConnectionTypeFTPS,
 411      kCFNetConnectionTypeFTPProxy,
 412      kCFNetConnectionTypeFTPSProxy
 413  };
 414  
 415  
 416  #if 0
 417  #pragma mark -
 418  #pragma mark CFStream Context
 419  #endif
 420  
 421  typedef struct {
 422      
 423      UInt32			_flags;
 424      
 425      CFURLRef			_url;
 426      CFURLRef			_newUrl;
 427      CFTypeRef			_dataStream;
 428      CFTypeRef			_userStream;	// CFReadStreamRef if GET; CFWriteStreamRef if PUT
 429      
 430      CFStreamError		_error;		// Currently just use for fallback from proxy to proxy
 431      
 432      CFSocketRef			_server;
 433      
 434      CFDictionaryRef		_attributes;
 435      long long			_offset;
 436      
 437      CFMutableArrayRef		_runloops;
 438      
 439      CFMutableDictionaryRef	_properties;
 440  
 441      CFReadStreamRef		_proxyStream;
 442      CFArrayRef			_proxies;
 443      CFIndex			_current;
 444      
 445      _CFNetConnectionRef		_connection;
 446      
 447  } _CFFTPStreamContext;
 448  
 449  
 450  #if 0
 451  #pragma mark -
 452  #pragma mark CFNetConnection Context
 453  #endif
 454  
 455  // This information needs to be carried over from request to request on a connection.	
 456  typedef struct {
 457      
 458      UInt32			_flags;
 459      
 460      _CFNetConnectionCacheKey	_key;		// Needed for stream creation
 461      
 462      UInt32			_result;
 463      _CFFTPStreamState		_state;
 464      CFStringRef			_root;		// This is the root directory (base url)
 465      
 466      CFIndex			_recvCount;	// Number of relevant bytes in the receive buffer
 467      CFIndex			_sendCount;	// Number of relevant bytes in the send buffer
 468      
 469      CFMutableDataRef		_recvBuffer;	// Used for leftovers (buffer can be larger than byte count)
 470      CFMutableDataRef		_sendBuffer;	// Used for leftovers (buffer can be larger than byte count)
 471      
 472  } _CFFTPNetConnectionContext;
 473  
 474  
 475  #if 0
 476  #pragma mark -
 477  #pragma mark Static Function Declarations
 478  #endif
 479  
 480  static void _FTPStreamFinalize(CFTypeRef stream, _CFFTPStreamContext* ctxt);
 481  static CFStringRef _FTPStreamCopyDescription(CFTypeRef stream, _CFFTPStreamContext* ctxt);
 482  static Boolean _FTPStreamOpen(CFTypeRef stream, CFStreamError* error, Boolean* openComplete, _CFFTPStreamContext* ctxt);
 483  static Boolean _FTPStreamOpenCompleted(CFTypeRef stream, CFStreamError* error, _CFFTPStreamContext* ctxt);
 484  static CFIndex _FTPStreamRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, _CFFTPStreamContext* ctxt);
 485  static Boolean _FTPStreamCanRead(CFReadStreamRef stream, _CFFTPStreamContext* ctxt);
 486  static CFIndex _FTPStreamWrite(CFWriteStreamRef stream, const UInt8* buffer, CFIndex bufferLength, CFStreamError* error, _CFFTPStreamContext* ctxt);
 487  static Boolean _FTPStreamCanWrite(CFWriteStreamRef stream, _CFFTPStreamContext* ctxt);
 488  static void _FTPStreamClose(CFTypeRef stream, _CFFTPStreamContext* ctxt);
 489  static CFTypeRef _FTPStreamCopyProperty(CFTypeRef stream, CFStringRef propertyName, _CFFTPStreamContext* ctxt);
 490  static Boolean _FTPStreamSetProperty(CFTypeRef stream, CFStringRef propertyName, CFTypeRef propertyValue, _CFFTPStreamContext* ctxt);
 491  static void _FTPStreamSchedule(CFTypeRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, _CFFTPStreamContext* ctxt);
 492  static void _FTPStreamUnschedule(CFTypeRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, _CFFTPStreamContext* ctxt);
 493  
 494  
 495  static const void* _CFFTPNetConnectionContextCreate(CFAllocatorRef alloc, const _CFFTPNetConnectionContext* template);
 496  static void _CFFTPNetConnectionContextFinalize(CFAllocatorRef alloc, const _CFFTPNetConnectionContext* ctxt);
 497  static CFStreamError _FTPConnectionCreateStreams(CFAllocatorRef alloc, const _CFFTPNetConnectionContext* key, CFWriteStreamRef* requestStream, CFReadStreamRef* responseStream);
 498  static void _FTPConnectionRequestStateChanged(_CFFTPStreamContext* ctxt, int newState, CFStreamError *err, _CFNetConnectionRef connection, _CFFTPNetConnectionContext* netCtxt);
 499  static void _FTPConnectionTransmitRequest(_CFFTPStreamContext* ctxt, _CFNetConnectionRef connection, _CFFTPNetConnectionContext* netCtxt);
 500  static void _FTPConnectionReceiveResponse(_CFFTPStreamContext* ctxt, _CFNetConnectionRef connection, _CFFTPNetConnectionContext* netCtxt);
 501  static void _FTPResponseStreamCallBack(_CFFTPStreamContext* ctxt, CFReadStreamRef stream, CFStreamEventType type, _CFNetConnectionRef conn, _CFFTPNetConnectionContext* netCtxt);
 502  static void _FTPRequestStreamCallBack(_CFFTPStreamContext* ctxt, CFWriteStreamRef stream, CFStreamEventType type, _CFNetConnectionRef conn, _CFFTPNetConnectionContext* netCtxt);
 503  static CFArrayRef _FTPRunLoopArrayCallBack(_CFFTPStreamContext *ctxt, _CFNetConnectionRef conn, _CFFTPNetConnectionContext *netCtxt);
 504  
 505  static Boolean _IsRoot(CFURLRef url);
 506  static void _FTPConnectionCacheCreate(void);
 507  static void _FTPConnectionCacheExpiration(_CFNetConnectionRef conn, CFDateRef expiration, CFMutableArrayRef list);
 508  static void _SetSOCKS4ProxyInformation(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt, CFURLRef proxyUrl);
 509  static void _SetSOCKS5ProxyInformation(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt, CFURLRef proxyUrl);
 510  static void _StartHTTPRequest(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt, CFStreamError* error, CFURLRef proxyUrl);
 511  static Boolean _ProcessHTTPResponse(_CFFTPStreamContext* ctxt, CFStreamError* error);
 512  static void _RollOverHTTPRequest(_CFFTPStreamContext* ctxt, CFStreamError* error);
 513  static void _CFStreamSocketCreatedCallBack(int fd, void* ctxt);
 514  static void _DataStreamCallBack(CFTypeRef stream, CFStreamEventType type, _CFFTPStreamContext* ctxt);
 515  static void _ReleaseDataReadStream(_CFFTPStreamContext* ctxt);
 516  static void _SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, _CFFTPStreamContext* ctxt);
 517  static void _StreamPropertyApplier(CFTypeRef key, CFTypeRef value, CFTypeRef stream);
 518  static Boolean _PASVAddressParser(const UInt8* buffer, struct sockaddr_in* saddr);
 519  static Boolean _EPSVPortParser(const UInt8* buffer, struct sockaddr_in6* saddr);
 520  static u_char _GetProtocolFamily(_CFFTPStreamContext* ctxt, UInt8* buffer);
 521  static Boolean _CreateListenerForContext(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt);
 522  static void _StartTransfer(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 523  static void _InvalidateServer(_CFFTPStreamContext* ctxt);
 524  static CFStringRef _CreatePathForContext(CFAllocatorRef alloc, _CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 525  
 526  static void _ReportError(_CFFTPStreamContext* ctxt, CFStreamError* error);
 527  static void _ConnectionComplete(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 528  static void _WriteCommand(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, CFStringRef cmd);
 529  static void _HandleResponse(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 530  static Boolean _ValidFTPString(CFStringRef theString);
 531  static CFURLRef _ConvertToCFFTPHappyURL(CFURLRef url);
 532  static Boolean _ReadModeBits(const UInt8* str, int* mode);
 533  static Boolean _ReadSize(const UInt8* str, UInt64* size);
 534  static CFStringRef _CFStringCreateCopyWithStrippedHTML(CFAllocatorRef alloc, CFStringRef theString);
 535  static const UInt8* _CFFTPGetDateTimeFunc(CFAllocatorRef alloc, const UInt8* str, CFIndex length, CFDateRef* date);
 536  static CFIndex _FindLine(const UInt8 *buffer, CFIndex bufferLength, const UInt8** start, const UInt8** end);
 537  
 538  #if defined(PROXY_PAC_SUPPORT)
 539  static void _ProxyStreamCallBack(CFReadStreamRef proxyStream, _CFFTPStreamContext* ctxt);
 540  #endif
 541  
 542  static void _AdvanceStateMachine(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length, Boolean isMultiLine);
 543  static void _HandleConnect(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length);
 544  static void _HandleUsername(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 545  static void _HandlePassword(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 546  static void _HandleSystem(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length);
 547  static void _HandleSiteDirStyle(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length);
 548  static void _HandleSiteTruth(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 549  static void _HandlePrintWorkingDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length);
 550  static void _HandleType(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 551  static void _HandleChangeDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 552  static void _HandlePassive(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length);
 553  static void _HandlePort(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 554  static void _HandleRestart(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 555  static void _HandleStat(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length, Boolean isMultiLine);
 556  static void _HandleSize(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length);
 557  static void _HandleRetrieve(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 558  static void _HandleNameList(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 559  static void _HandleList(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 560  static void _HandleStore(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 561  static void _HandleMakeDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 562  static void _HandleRemoveDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 563  static void _HandleDelete(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 564  static void _HandleRenameFrom(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 565  static void _HandleRenameTo(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 566  static void _StartProcess(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt);
 567  
 568  
 569  #if 0
 570  #pragma mark -
 571  #pragma mark Extern Function Declarations
 572  #endif
 573  
 574  extern void _CFSocketStreamCreatePair(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFSocketNativeHandle s,
 575  									  const CFSocketSignature* sig, CFReadStreamRef* readStream, CFWriteStreamRef* writeStream);
 576  
 577  
 578  #if 0
 579  #pragma mark -
 580  #pragma mark Globals
 581  #endif
 582  
 583  static CFMutableDictionaryRef gFTPConnectionTimeouts = NULL;
 584  static CFNetConnectionCacheRef gFTPConnectionCache = NULL;
 585  static CFSpinLock_t gFTPSpinLock = 0;
 586  static _CFNetConnectionCallBacks* _kFTPConnectionCallBacks = NULL;
 587  
 588  
 589  #if 0
 590  #pragma mark -
 591  #pragma mark CFStream Callback Functions
 592  #endif
 593  
 594  
 595  /* static */ void
 596  _FTPStreamFinalize(CFTypeRef stream, _CFFTPStreamContext* ctxt) {
 597  
 598      _FTPStreamClose(stream, ctxt);
 599  
 600      CFRelease(ctxt->_url);
 601  
 602      if (ctxt->_newUrl)
 603          CFRelease(ctxt->_newUrl);
 604  
 605      
 606      
 607      CFRelease(ctxt->_runloops);
 608      CFRelease(ctxt->_properties);
 609  
 610      if (ctxt->_proxies)
 611          CFRelease(ctxt->_proxies);
 612      
 613      if (ctxt->_attributes)
 614          CFRelease(ctxt->_attributes);
 615  
 616      CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
 617  }
 618  
 619  
 620  /* static */ CFStringRef
 621  _FTPStreamCopyDescription(CFTypeRef stream, _CFFTPStreamContext* ctxt) {
 622  
 623      // **FIXME** Should display whether it's reading or writing.
 624      // **FIXME** Should display URL.
 625  
 626      return CFStringCreateWithFormat(CFGetAllocator(stream),
 627                                      NULL,
 628                                      kCFFTPStreamDescriptionFormat,
 629                                      (int)stream,
 630                                      __CFBitIsSet(ctxt->_flags, kFlagBitPerformUpload) ? kCFFTPStreamUploadDescription : kCFFTPStreamDownloadDescription,
 631                                      ctxt->_url,
 632                                      ctxt->_flags);
 633  }
 634  
 635  
 636  /* static */ Boolean
 637  _FTPStreamOpen(CFTypeRef stream, CFStreamError* error, Boolean* openComplete,
 638  			   _CFFTPStreamContext* ctxt)
 639  {
 640      CFAllocatorRef alloc = CFGetAllocator(stream);
 641      
 642      UInt32 type = kCFNetConnectionTypeFTP;
 643      
 644      _CFFTPNetConnectionContext template;
 645  
 646      CFTypeRef proxyUrl = NULL;
 647      CFStringRef proxyHost = NULL;
 648  
 649      CFBooleanRef persistent = (CFBooleanRef)CFDictionaryGetValue(ctxt->_properties,
 650                                                                  kCFStreamPropertyFTPAttemptPersistentConnection);
 651      Boolean usePersistent = (persistent && CFEqual(persistent, kCFBooleanFalse)) ? FALSE : TRUE;
 652  
 653      CFStringRef scheme = CFURLCopyScheme(ctxt->_url);
 654      SInt32 port = CFURLGetPortNumber(ctxt->_url);
 655      CFStringRef host = CFURLCopyHostName(ctxt->_url);    
 656  
 657      if (!ctxt->_proxies) {
 658  
 659          
 660          CFDictionaryRef info = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPProxy);
 661          if (info) {
 662  			CFRetain(info);
 663              CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertyFTPProxy);
 664  		}
 665          else {
 666              info = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
 667              if (info) {
 668  				CFRetain(info);
 669                  CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
 670  			}
 671          }
 672  #if !defined(PROXY_PAC_SUPPORT)
 673          void* _ProxyStreamCallBack = NULL;
 674  #endif
 675          ctxt->_proxies = _CFNetworkFindProxyForURLAsync(NULL,			// Take the scheme from the url
 676                                                          ctxt->_url,
 677                                                          NULL,	 		// Take the host from the url
 678                                                          info,			// Proxy dictionary.
 679                                                          (_CFProxyStreamCallBack)_ProxyStreamCallBack,
 680                                                          ctxt,
 681                                                          &ctxt->_proxyStream);
 682  		if (info)
 683  			CFRelease(info);
 684  		
 685          ctxt->_current = 0;
 686  
 687          // If ctxt->_proxies is NULL, there has been an error and
 688          // need to error out the stream now.
 689          if (!ctxt->_proxies) {
 690              
 691              CFRelease(scheme);
 692              CFRelease(host);
 693  
 694              if (ctxt->_proxyStream) {
 695                  
 696  				_CFTypeScheduleOnMultipleRunLoops(ctxt->_proxyStream, ctxt->_runloops);
 697  
 698                  *openComplete = FALSE;
 699                  return TRUE;
 700              }
 701              else {
 702                  *openComplete = TRUE;
 703  		error->domain = _kCFStreamErrorDomainNativeSockets;
 704                  error->error = ENOTCONN;
 705                  return FALSE;
 706              }
 707          }
 708      }
 709  
 710      // If ctxt->_current is beyond the end,  there has been an
 711      // error and must error out the stream now.
 712      if (ctxt->_current == CFArrayGetCount(ctxt->_proxies)) {
 713          CFRelease(scheme);
 714          CFRelease(host);
 715          *openComplete = TRUE;
 716          if (ctxt->_error.error)
 717              *error = ctxt->_error;
 718          else {
 719              error->domain = _kCFStreamErrorDomainNativeSockets;
 720              error->error = ENOTCONN;
 721          }
 722          return FALSE;
 723      }
 724  
 725      ctxt->_error.domain = 0;
 726      ctxt->_error.error = 0;
 727      
 728      CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertyFTPProxy);
 729      CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
 730      __CFBitClear(ctxt->_flags, kFlagBitReadHTTPResponse);
 731      __CFBitClear(ctxt->_flags, kFlagBitIsHTTPRequest);
 732      
 733      proxyUrl = (CFTypeRef)CFArrayGetValueAtIndex(ctxt->_proxies, ctxt->_current);
 734      
 735      if (!CFEqual(proxyUrl, kCFNull)) {
 736  
 737          CFStringRef pScheme = CFURLCopyScheme(proxyUrl);
 738  
 739          if (CFEqual(pScheme, kFTPSchemeString)) {
 740  
 741              CFStringRef pHost = CFURLCopyHostName(proxyUrl);
 742              SInt32 p = CFURLGetPortNumber(proxyUrl);
 743              CFNumberRef pPort = CFNumberCreate(alloc, kCFNumberSInt32Type, &p);
 744              CFMutableDictionaryRef pInfo = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
 745              
 746              CFDictionaryAddValue(pInfo, kCFStreamPropertyFTPProxyHost, pHost);
 747              CFDictionaryAddValue(pInfo, kCFStreamPropertyFTPProxyPort, pPort);
 748              
 749              CFDictionaryAddValue(ctxt->_properties, kCFStreamPropertyFTPProxy, pInfo);
 750              CFRelease(pInfo);
 751              
 752              proxyHost = pHost;
 753              
 754              CFRelease(pHost);
 755              CFRelease(pPort);
 756          }
 757  
 758          else if (CFEqual(pScheme, kSOCKS4SchemeString))
 759              _SetSOCKS4ProxyInformation(alloc, ctxt, proxyUrl);
 760  
 761          else if (CFEqual(pScheme, kSOCKS5SchemeString))
 762              _SetSOCKS5ProxyInformation(alloc, ctxt, proxyUrl);
 763  		
 764          else if ((CFStringCompare(pScheme, kHTTPSchemeString, kCFCompareCaseInsensitive) == kCFCompareEqualTo) ||
 765                   (CFStringCompare(pScheme, kHTTPSSchemeString, kCFCompareCaseInsensitive) == kCFCompareEqualTo))
 766          {
 767              CFRelease(pScheme);
 768              CFRelease(scheme);
 769  
 770              // **FIXME** Upload through an HTTP proxy is not supported.
 771              if (__CFBitIsSet(ctxt->_flags, kFlagBitPerformUpload)) {
 772                  ctxt->_current++;
 773                  return _FTPStreamOpen(stream, error, openComplete, ctxt);
 774              }
 775  
 776              _StartHTTPRequest(alloc, ctxt, error, proxyUrl);
 777              if (error->error) {
 778                  *openComplete = TRUE;
 779                  return FALSE;
 780              }
 781  
 782              return TRUE;
 783          }
 784  
 785          CFRelease(pScheme);
 786      }
 787      
 788      if (CFStringCompare(scheme, kFTPSchemeString, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
 789          if (port == -1)
 790              port = 21;
 791              
 792          if (proxyHost)
 793              type = kCFNetConnectionTypeFTPProxy;
 794      }
 795      else {
 796          
 797          CFTypeRef ssl = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketSecurityLevel);
 798          
 799          if (!ssl)
 800              _FTPStreamSetProperty(stream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL, ctxt);
 801              
 802          if (port == -1)
 803              port = 990;
 804              
 805          if (proxyHost)
 806              type = kCFNetConnectionTypeFTPSProxy;
 807          else
 808              type = kCFNetConnectionTypeFTPS;
 809      }
 810  
 811      CFRelease(scheme);
 812                  
 813      *openComplete = FALSE;
 814      memset(error, 0, sizeof(error[0]));
 815      
 816      if (!ctxt->_connection) {
 817          
 818          CFMutableArrayRef expired = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
 819          _CFNetConnectionCacheKey key = createConnectionCacheKey(host, port, type, ctxt->_properties);
 820      
 821          memset(&template, 0, sizeof(template));
 822          template._state = kFTPStateConnect;
 823          template._key = key;
 824  
 825          __CFSpinLock(&gFTPSpinLock);
 826          if (gFTPConnectionCache == NULL)
 827              _FTPConnectionCacheCreate();
 828          __CFSpinUnlock(&gFTPSpinLock);
 829  
 830          lockConnectionCache(gFTPConnectionCache);
 831          CFDictionaryApplyFunction( gFTPConnectionTimeouts, (CFDictionaryApplierFunction)_FTPConnectionCacheExpiration, expired);
 832          unlockConnectionCache(gFTPConnectionCache);
 833          
 834          ctxt->_connection = findOrCreateNetConnection(gFTPConnectionCache,
 835                                                        alloc,
 836                                                        _kFTPConnectionCallBacks,
 837                                                        &template,
 838                                                        key,
 839                                                        usePersistent,
 840                                                        ctxt->_properties);
 841          
 842          lockConnectionCache(gFTPConnectionCache);
 843          CFDictionaryRemoveValue(gFTPConnectionTimeouts, ctxt->_connection);
 844          if (CFArrayGetCount(expired)) {
 845              CFIndex i;
 846              for (i = CFArrayGetCount(expired) - 1; i >= 0; i--) {
 847  
 848                  _CFNetConnectionRef conn = (_CFNetConnectionRef)CFArrayGetValueAtIndex(expired, i);
 849                  CFDictionaryRemoveValue(gFTPConnectionTimeouts, conn);
 850                  if (conn != ctxt->_connection)
 851                      _CFNetConnectionSetAllowsNewRequests(conn, FALSE);
 852              }
 853          }
 854          CFRelease(expired);
 855          unlockConnectionCache(gFTPConnectionCache);
 856          
 857          releaseConnectionCacheKey(key);
 858      }
 859      
 860      CFRelease(host);
 861      
 862      if (!ctxt->_connection) {
 863          *openComplete = TRUE;
 864          error->error = errno;
 865          if (!error->error)
 866              error->error = ENOMEM;
 867          error->domain = kCFStreamErrorDomainPOSIX;
 868          return FALSE;
 869      }
 870  
 871      // Detect failed enque and handle properly.
 872      if (!_CFNetConnectionEnqueue(ctxt->_connection, ctxt)) {
 873          *openComplete = TRUE;
 874          error->error = errno;
 875          if (!error->error)
 876              error->error = ENOMEM;
 877          error->domain = kCFStreamErrorDomainPOSIX;
 878          return FALSE;
 879      } else if (!usePersistent) {
 880          _CFNetConnectionSetAllowsNewRequests(ctxt->_connection, FALSE);
 881      }
 882  
 883      return TRUE;
 884  }
 885  
 886  
 887  /* static */ Boolean
 888  _FTPStreamOpenCompleted(CFTypeRef stream, CFStreamError* error, _CFFTPStreamContext* ctxt) {
 889  
 890      Boolean result = FALSE;
 891      memset(error, 0, sizeof(error[0]));
 892  
 893  #if defined(PROXY_PAC_SUPPORT)
 894      if (ctxt->_proxyStream)
 895          _ProxyStreamCallBack(ctxt->_proxyStream, ctxt);
 896  #endif
 897      
 898      if (ctxt->_dataStream) {
 899      
 900          CFStreamStatus status;
 901          CFTypeID i = CFReadStreamGetTypeID();
 902          
 903          if (CFGetTypeID(ctxt->_dataStream) == i)
 904              status = CFReadStreamGetStatus((CFReadStreamRef)ctxt->_dataStream);
 905          else
 906              status = CFWriteStreamGetStatus((CFWriteStreamRef)ctxt->_dataStream);
 907          
 908          switch (status) {
 909              
 910              case kCFStreamStatusNotOpen:
 911              case kCFStreamStatusOpening:
 912                  break;
 913                  
 914              case kCFStreamStatusError:
 915                  if (CFGetTypeID(stream) == i)
 916                      *error = CFReadStreamGetError((CFReadStreamRef)stream);
 917                  else
 918                      *error = CFWriteStreamGetError((CFWriteStreamRef)stream);
 919                  return TRUE;
 920                  
 921              default:
 922                  return TRUE;
 923          }
 924      }
 925  
 926      if (ctxt->_connection) {
 927          _CFNetConnectionGetState(ctxt->_connection, TRUE, ctxt);
 928          if (ctxt->_connection &&
 929              (_CFNetConnectionGetCurrentRequest(ctxt->_connection) == ctxt))
 930          {
 931              CFReadStreamRef rStream = _CFNetConnectionGetResponseStream(ctxt->_connection);
 932              CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(ctxt->_connection);
 933  
 934              memset(error, 0, sizeof(error[0]));
 935  
 936              if (rStream && (CFReadStreamGetStatus(rStream) == kCFStreamStatusError)) {
 937                  *error = CFReadStreamGetError(rStream);
 938                  if (ctxt->_connection &&
 939                      (((_CFFTPNetConnectionContext*)_CFNetConnectionGetInfoPointer(ctxt->_connection))->_state == kFTPStateConnect) &&
 940                      (ctxt->_current < CFArrayGetCount(ctxt->_proxies)))
 941                  {
 942                      ctxt->_current++;
 943                      ctxt->_error = *error;
 944                      _CFNetConnectionErrorOccurred(ctxt->_connection, error);
 945                      return result;
 946                  }
 947                  else {
 948                      result = TRUE;
 949                      _ReportError(ctxt, error);
 950                  }
 951              }
 952              else if (wStream && (CFWriteStreamGetStatus(wStream) == kCFStreamStatusError)) {
 953                  *error = CFWriteStreamGetError(wStream);
 954                  if (ctxt->_connection &&
 955                      (((_CFFTPNetConnectionContext*)_CFNetConnectionGetInfoPointer(ctxt->_connection))->_state == kFTPStateConnect) &&
 956                      (ctxt->_current < CFArrayGetCount(ctxt->_proxies)))
 957                  {
 958                      ctxt->_current++;
 959                      ctxt->_error = *error;
 960                      _CFNetConnectionErrorOccurred(ctxt->_connection, error);
 961                      return result;
 962                  }
 963                  else {
 964                      result = TRUE;
 965                      _ReportError(ctxt, error);
 966                  }
 967              }
 968          }
 969      }
 970  
 971      if (ctxt->_server) {
 972          CFRunLoopSourceRef src = CFSocketCreateRunLoopSource(CFGetAllocator(ctxt->_server), ctxt->_server, 0);
 973  
 974          if (src) {
 975              CFRunLoopRef rl = CFRunLoopGetCurrent();
 976              
 977              CFRunLoopAddSource(rl, src, kCFFTPStreamOpenCompleted);
 978              
 979              CFRunLoopRunInMode(kCFFTPStreamOpenCompleted, 0.0, TRUE);
 980              
 981              CFRunLoopRemoveSource(rl, src, kCFFTPStreamOpenCompleted);
 982              
 983              CFRelease(src);
 984          }
 985      }
 986      
 987      return result;
 988  }
 989  
 990  
 991  /* static */ CFIndex
 992  _FTPStreamRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error,
 993  			   Boolean* atEOF, _CFFTPStreamContext* ctxt)
 994  {
 995  	CFIndex result = 0;
 996  	
 997  	*atEOF = FALSE;
 998  	memset(error, 0, sizeof(error[0]));
 999  
1000          if (ctxt->_proxyStream) {
1001  
1002              CFRunLoopRef rl = CFRunLoopGetCurrent();
1003              CFReadStreamRef s = (CFReadStreamRef)CFRetain(ctxt->_proxyStream);
1004  
1005              CFReadStreamScheduleWithRunLoop(s, rl, kCFFTPStreamOpenCompleted);
1006  
1007              do {
1008                  CFRunLoopRunInMode(kCFFTPStreamOpenCompleted, 1e+20, TRUE);
1009              } while (ctxt->_proxyStream);
1010  
1011              CFReadStreamUnscheduleFromRunLoop(s, rl, kCFFTPStreamOpenCompleted);
1012              CFRelease(s);
1013          }
1014          
1015  	while (ctxt->_connection && (!ctxt->_dataStream || !CFReadStreamHasBytesAvailable((CFReadStreamRef)ctxt->_dataStream))) {
1016      
1017          CFWriteStreamRef requestStreams;
1018          CFReadStreamRef responseStreams;
1019      
1020          _CFNetConnectionGetState(ctxt->_connection, TRUE, ctxt);
1021      
1022          if (!ctxt->_connection) {
1023              *error = CFReadStreamGetError((CFReadStreamRef)ctxt->_userStream);
1024              if (error->error) {
1025                  *atEOF = TRUE;
1026                  result = -1;
1027              }
1028              break;
1029          }
1030  
1031          else {
1032  			
1033              requestStreams = _CFNetConnectionGetRequestStream(ctxt->_connection);
1034              responseStreams = _CFNetConnectionGetResponseStream(ctxt->_connection);
1035          
1036              if (responseStreams) {
1037                  *error = CFReadStreamGetError(responseStreams);
1038              }
1039              
1040              if (!error->error && requestStreams) {
1041                  *error = CFWriteStreamGetError(requestStreams);
1042              }
1043              
1044              if (error->error) {
1045  
1046                  if ((((_CFFTPNetConnectionContext*)_CFNetConnectionGetInfoPointer(ctxt->_connection))->_state == kFTPStateConnect) &&
1047                      (ctxt->_current < CFArrayGetCount(ctxt->_proxies)))
1048                  {
1049                      ctxt->_current++;
1050                      ctxt->_error = *error;
1051                      _CFNetConnectionErrorOccurred(ctxt->_connection, error);
1052                      continue;
1053                  }
1054  
1055                  else {
1056                      result = -1;
1057                      *atEOF = TRUE;
1058                      break;
1059                  }
1060              }
1061          }
1062      }
1063  		
1064      if (ctxt->_dataStream) {
1065  
1066          result = CFReadStreamRead((CFReadStreamRef)ctxt->_dataStream, buffer, bufferLength);
1067  
1068          if (__CFBitIsSet(ctxt->_flags, kFlagBitIsHTTPRequest) && !__CFBitIsSet(ctxt->_flags, kFlagBitReadHTTPResponse)) {
1069  
1070              if ((result >= 0) && _ProcessHTTPResponse(ctxt, error)) {
1071  
1072                  if (error->error) {
1073                      *atEOF = TRUE;	// Return here so error isn't retrieved from the stream.
1074                      return result;	// HTTP streams don't report HTTP responses as errors (FTP does).
1075                  }
1076                  else if (__CFBitIsSet(ctxt->_flags, kFlagBit407TriedOnce)) {
1077  
1078                      // Had to re-open a new connection in order to do the read.  Return the results of the
1079                      // read on the new connection.
1080                      return _FTPStreamRead((CFReadStreamRef)ctxt->_userStream, buffer, bufferLength, error, atEOF, ctxt);
1081                  }
1082              }
1083              else if (ctxt->_current < CFArrayGetCount(ctxt->_proxies)) {
1084  
1085                  _RollOverHTTPRequest(ctxt, error);
1086  
1087                  if (error->error) {
1088                      *atEOF = TRUE;	// Return here so error isn't retrieved from the stream.
1089                      return result;	// HTTP streams don't report HTTP responses as errors (FTP does).
1090                  }
1091                  else {
1092  
1093                      // Return the results of the read on the new connection.
1094                      return _FTPStreamRead((CFReadStreamRef)ctxt->_userStream, buffer, bufferLength, error, atEOF, ctxt);
1095                  }
1096              }
1097          }
1098  
1099          if (result <= 0) {
1100  
1101  	    // has the RETR command completed yet?
1102  	    if (__CFBitIsSet(ctxt->_flags, kFlagBitCompleteDeferred)) {
1103  		// yes, so the response is complete
1104  		_CFNetConnectionResponseIsComplete(ctxt->_connection, ctxt);
1105  		__CFBitClear(ctxt->_flags, kFlagBitCompleteDeferred);
1106  	    }
1107  	    else {
1108  		__CFBitSet(ctxt->_flags, kFlagBitCompleteDeferred);
1109  	    }
1110  
1111              // **FIXME** This is not 100% correct.  If the data stream has zero bytes,
1112              // the result should actually be read from the control stream and any error
1113              // should be processed there.  This should probably call CFConnectionGetState
1114              // and pump along the state machine until connection is done.  The problem
1115              // with that solution is that a blocking situation occurs.
1116  
1117              *atEOF = TRUE;
1118              *error = CFReadStreamGetError((CFReadStreamRef)ctxt->_dataStream);
1119          }
1120      }
1121      
1122      return result;
1123  }
1124  
1125  
1126  /* static */ Boolean
1127  _FTPStreamCanRead(CFReadStreamRef stream, _CFFTPStreamContext* ctxt) {
1128  
1129      Boolean result = FALSE;
1130  
1131  #if defined(PROXY_PAC_SUPPORT)
1132      if (ctxt->_proxyStream)
1133          _ProxyStreamCallBack(ctxt->_proxyStream, ctxt);
1134  #endif
1135      
1136      if (ctxt->_connection) {
1137  
1138          _CFNetConnectionGetState(ctxt->_connection, TRUE, ctxt);
1139  
1140          if (!ctxt->_connection) {
1141              CFStreamError error = CFReadStreamGetError((CFReadStreamRef)ctxt->_userStream);
1142              if (error.error) {
1143                  CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, &error);
1144                  result = TRUE;
1145              }
1146          }
1147  
1148          else {
1149  
1150              CFStreamError error;
1151              CFWriteStreamRef requestStreams = _CFNetConnectionGetRequestStream(ctxt->_connection);
1152              CFReadStreamRef responseStreams = _CFNetConnectionGetResponseStream(ctxt->_connection);
1153  
1154              if (responseStreams) {
1155                  error = CFReadStreamGetError(responseStreams);
1156              }
1157  
1158              if (!error.error && requestStreams) {
1159                  error = CFWriteStreamGetError(requestStreams);
1160              }
1161  
1162              if (error.error) {
1163  
1164                  if ((((_CFFTPNetConnectionContext*)_CFNetConnectionGetInfoPointer(ctxt->_connection))->_state == kFTPStateConnect) &&
1165                      (ctxt->_current < CFArrayGetCount(ctxt->_proxies)))
1166                  {
1167                      ctxt->_current++;
1168                      ctxt->_error = error;
1169                      _CFNetConnectionErrorOccurred(ctxt->_connection, &error);
1170                      result = FALSE;
1171                  }
1172  
1173                  else {
1174                      CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, &error);
1175                      result = TRUE;
1176                  }
1177              }
1178          }
1179      }
1180  	
1181      if (ctxt->_dataStream) {
1182  
1183          result = CFReadStreamHasBytesAvailable((CFReadStreamRef)ctxt->_dataStream);
1184  
1185          // **FIXME** Catch situation where CFReadStreamHasBytesAvailable returns FALSE
1186          // and HTTP stream is at the end.
1187          if (!result && CFReadStreamGetStatus((CFReadStreamRef)ctxt->_dataStream) == kCFStreamStatusAtEnd)
1188              result = TRUE;
1189  		
1190          if (result && __CFBitIsSet(ctxt->_flags, kFlagBitIsHTTPRequest) && !__CFBitIsSet(ctxt->_flags, kFlagBitReadHTTPResponse)) {
1191  
1192              CFStreamError error;
1193  
1194              if (_ProcessHTTPResponse(ctxt, &error)) {
1195  
1196                  if (error.error)
1197                      CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, &error);
1198                  else
1199                      result = FALSE;
1200              }
1201              else if (ctxt->_current < CFArrayGetCount(ctxt->_proxies)) {
1202  
1203                  _RollOverHTTPRequest(ctxt, &error);
1204  
1205                  if (error.error)
1206                      CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, &error);
1207  
1208                  else
1209                      result = FALSE;
1210              }
1211          }
1212      }
1213  
1214      return result;
1215  }
1216  
1217  
1218  /* static */ CFIndex
1219  _FTPStreamWrite(CFWriteStreamRef stream, const UInt8* buffer, CFIndex bufferLength,
1220                  CFStreamError* error, _CFFTPStreamContext* ctxt)
1221  {
1222  	CFIndex result = 0;
1223  	memset(error, 0, sizeof(error[0]));
1224  
1225          if (ctxt->_proxyStream) {
1226  
1227              CFRunLoopRef rl = CFRunLoopGetCurrent();
1228              CFReadStreamRef s = (CFReadStreamRef)CFRetain(ctxt->_proxyStream);
1229  
1230              CFReadStreamScheduleWithRunLoop(s, rl, kCFFTPStreamOpenCompleted);
1231  
1232              do {
1233                  CFRunLoopRunInMode(kCFFTPStreamOpenCompleted, 1e+20, TRUE);
1234              } while (ctxt->_proxyStream);
1235  
1236              CFReadStreamUnscheduleFromRunLoop(s, rl, kCFFTPStreamOpenCompleted);
1237              CFRelease(s);
1238          }
1239          
1240  	while (ctxt->_connection && (!ctxt->_dataStream || !CFWriteStreamCanAcceptBytes((CFWriteStreamRef)ctxt->_dataStream))) {
1241      
1242          CFWriteStreamRef requestStreams;
1243          CFReadStreamRef responseStreams;
1244      
1245          _CFNetConnectionGetState(ctxt->_connection, TRUE, ctxt);
1246      
1247          if (!ctxt->_connection) {
1248              *error = CFWriteStreamGetError((CFWriteStreamRef)ctxt->_userStream);
1249              if (error->error)
1250                  result = -1;
1251              break;
1252          }
1253  
1254          else {
1255              requestStreams = _CFNetConnectionGetRequestStream(ctxt->_connection);
1256              responseStreams = _CFNetConnectionGetResponseStream(ctxt->_connection);
1257          
1258              if (responseStreams) {
1259                  *error = CFReadStreamGetError(responseStreams);
1260              }
1261              
1262              if (!error->error && requestStreams) {
1263                  *error = CFWriteStreamGetError(requestStreams);
1264              }
1265              
1266              if (error->error) {
1267                  result = -1;
1268                  break;
1269              }
1270          }
1271      }
1272  
1273      if (ctxt->_dataStream) {
1274  
1275          result = CFWriteStreamWrite((CFWriteStreamRef)ctxt->_dataStream, buffer, bufferLength);
1276  
1277          if (result <= 0) {
1278  
1279              // **FIXME** This is not 100% correct.  If the data stream has zero bytes,
1280              // the result should actually be read from the control stream and any error
1281              // should be processed there.  This should probably call CFConnectionGetState
1282              // and pump along the state machine until connection is done.  The problem
1283              // with that solution is that a blocking situation occurs.
1284  
1285              *error = CFWriteStreamGetError((CFWriteStreamRef)ctxt->_dataStream);
1286          }
1287      }
1288      
1289      return result;
1290  }
1291  
1292  
1293  /* static */ Boolean
1294  _FTPStreamCanWrite(CFWriteStreamRef stream, _CFFTPStreamContext* ctxt) {
1295  
1296      Boolean result = FALSE;
1297  
1298  #if defined(PROXY_PAC_SUPPORT)
1299      if (ctxt->_proxyStream)
1300          _ProxyStreamCallBack(ctxt->_proxyStream, ctxt);
1301  #endif
1302      
1303      if (ctxt->_connection) {
1304  
1305          _CFNetConnectionGetState(ctxt->_connection, TRUE, ctxt);
1306  
1307          if (!ctxt->_connection) {
1308              CFStreamError error = CFWriteStreamGetError((CFWriteStreamRef)ctxt->_userStream);
1309              if (error.error) {
1310                  CFWriteStreamSignalEvent((CFWriteStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, &error);
1311                  result = TRUE;
1312              }
1313          }
1314  
1315          else {
1316  
1317              CFStreamError error;
1318              CFWriteStreamRef requestStreams = _CFNetConnectionGetRequestStream(ctxt->_connection);
1319              CFReadStreamRef responseStreams = _CFNetConnectionGetResponseStream(ctxt->_connection);
1320  
1321              if (responseStreams) {
1322                  error = CFReadStreamGetError(responseStreams);
1323              }
1324  
1325              if (!error.error && requestStreams) {
1326                  error = CFWriteStreamGetError(requestStreams);
1327              }
1328  
1329              if (error.error) {
1330  
1331                  if ((((_CFFTPNetConnectionContext*)_CFNetConnectionGetInfoPointer(ctxt->_connection))->_state == kFTPStateConnect) &&
1332                      (ctxt->_current < CFArrayGetCount(ctxt->_proxies)))
1333                  {
1334                      ctxt->_current++;
1335                      ctxt->_error = error;
1336                      _CFNetConnectionErrorOccurred(ctxt->_connection, &error);
1337                      result = FALSE;
1338                  }
1339  
1340                  else {
1341                      CFWriteStreamSignalEvent((CFWriteStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, &error);
1342                      result = TRUE;
1343                  }
1344              }
1345          }
1346      }
1347          
1348      if (ctxt->_dataStream)
1349          return CFWriteStreamCanAcceptBytes((CFWriteStreamRef)ctxt->_dataStream);
1350  
1351      return FALSE;
1352  }
1353  
1354  
1355  /* static */ void
1356  _FTPStreamClose(CFTypeRef stream, _CFFTPStreamContext* ctxt) {
1357  
1358      _InvalidateServer(ctxt);
1359  
1360      if (ctxt->_proxyStream) {
1361  	
1362  		_CFTypeUnscheduleFromMultipleRunLoops(ctxt->_proxyStream, ctxt->_runloops);
1363  
1364          CFReadStreamClose(ctxt->_proxyStream);
1365          CFRelease(ctxt->_proxyStream);
1366          ctxt->_proxyStream = NULL;
1367      }
1368      
1369      if (ctxt->_dataStream) {
1370  
1371          if (CFGetTypeID(ctxt->_dataStream) == CFReadStreamGetTypeID())
1372              _ReleaseDataReadStream(ctxt);
1373  
1374          else {
1375  
1376  			_CFTypeInvalidate(ctxt->_dataStream);
1377  			_CFTypeUnscheduleFromMultipleRunLoops(ctxt->_dataStream, ctxt->_runloops);
1378              
1379              CFWriteStreamClose((CFWriteStreamRef)(ctxt->_dataStream));
1380              CFRelease(ctxt->_dataStream);
1381              ctxt->_dataStream = NULL;
1382          }
1383      }
1384  	
1385      if (ctxt->_connection) {
1386          
1387          int state = _CFNetConnectionGetState(ctxt->_connection, FALSE, ctxt);
1388          
1389          if (state != kTransmittingRequest)
1390              _CFNetConnectionDequeue(ctxt->_connection, ctxt);
1391          else {
1392              CFArrayRef a = ctxt->_runloops;
1393              int i, count = CFArrayGetCount(a);
1394              for (i = 0; i < count; i += 2) {
1395                  _CFNetConnectionUnschedule(ctxt->_connection,
1396                                             ctxt,
1397                                             (CFRunLoopRef)CFArrayGetValueAtIndex(a, i),
1398                                             (CFStringRef)CFArrayGetValueAtIndex(a, i + 1));
1399              }
1400              
1401              _CFNetConnectionRequestIsComplete(ctxt->_connection, ctxt);
1402              _CFNetConnectionResponseIsComplete(ctxt->_connection, ctxt);
1403          }
1404      }
1405  }
1406  
1407  
1408  /* static */ CFTypeRef
1409  _FTPStreamCopyProperty(CFTypeRef stream, CFStringRef propertyName, _CFFTPStreamContext* ctxt) {
1410  
1411      CFTypeRef value = NULL;
1412      
1413      if (CFEqual(propertyName, kCFStreamPropertyFTPUsePassiveMode)) {
1414          value = CFRetain(__CFBitIsSet(ctxt->_flags, kFlagBitPerformPASV) ? kCFBooleanTrue : kCFBooleanFalse);
1415      }
1416  	
1417      else if (CFEqual(propertyName, kCFStreamPropertyFTPFetchResourceInfo)) {
1418          value = CFRetain(__CFBitIsSet(ctxt->_flags, kFlagBitPerformSTAT) ? kCFBooleanTrue : kCFBooleanFalse);
1419      }
1420  	
1421      else if (CFEqual(propertyName, kCFStreamPropertyFTPFetchNameList)) {
1422          value = CFRetain(__CFBitIsSet(ctxt->_flags, kFlagBitPerformNLST) ? kCFBooleanTrue : kCFBooleanFalse);
1423      }
1424  	
1425      else if (CFEqual(propertyName, kCFStreamPropertyFTPFileTransferOffset)) {
1426          value = CFNumberCreate(CFGetAllocator(ctxt->_properties), kCFNumberLongLongType, &ctxt->_offset);
1427      }
1428  	
1429      else if (CFEqual(propertyName, kCFStreamPropertyFTPResourceSize)) {
1430          if (ctxt->_attributes) {
1431              CFNumberRef original = CFDictionaryGetValue(ctxt->_attributes, kCFFTPResourceSize);
1432              if (original)
1433                  value = CFRetain(original);		// Becky's sez that CFNumber is immutable so retaining is fine.
1434          }
1435      }
1436  
1437      else if (CFEqual(propertyName, kCFStreamPropertyFTPAttemptPersistentConnection)) {
1438          CFBooleanRef contains = CFDictionaryGetValue(ctxt->_properties, propertyName);
1439          value = contains ? CFRetain(contains) : CFRetain(kCFBooleanTrue);
1440      }
1441      
1442      else if (CFEqual(propertyName, _kCFStreamPropertyFTPLogInOnly)) {
1443          value = CFRetain(__CFBitIsSet(ctxt->_flags, kFlagBitLogInOnly) ? kCFBooleanTrue : kCFBooleanFalse);
1444      }
1445      
1446      else if (CFEqual(propertyName, _kCFStreamPropertyFTPRemoveResource)) {
1447          value = CFRetain(__CFBitIsSet(ctxt->_flags, kFlagBitRemoveResource) ? kCFBooleanTrue : kCFBooleanFalse);
1448      }
1449      
1450      else if (CFEqual(propertyName, _kCFStreamPropertyFTPNewResourceName)) {
1451          value = ctxt->_newUrl ? CFRetain(ctxt->_newUrl) : NULL;
1452      }
1453  
1454      if (!value && ctxt->_dataStream) {
1455          if (CFGetTypeID(ctxt->_dataStream) == CFReadStreamGetTypeID())
1456              value = CFReadStreamCopyProperty((CFReadStreamRef)ctxt->_dataStream, propertyName);
1457          else
1458              value = CFWriteStreamCopyProperty((CFWriteStreamRef)ctxt->_dataStream, propertyName);
1459      }
1460  
1461      // If there is a control stream, check it for the property.
1462      if (!value && ctxt->_connection &&
1463          (_CFNetConnectionGetCurrentRequest(ctxt->_connection) == ctxt))
1464      {
1465          CFTypeRef s = _CFNetConnectionGetResponseStream(ctxt->_connection);
1466          
1467          if (s)
1468              value = CFReadStreamCopyProperty((CFReadStreamRef)s, propertyName);
1469              
1470          if (!value) {
1471              s = _CFNetConnectionGetRequestStream(ctxt->_connection);
1472              if (s)
1473                  value = CFWriteStreamCopyProperty((CFWriteStreamRef)s, propertyName);
1474          }
1475      }
1476  
1477      // If every avenue has been tried, try grabbing from the local properties.
1478      if (!value) {
1479      
1480          CFTypeRef orig = CFDictionaryGetValue(ctxt->_properties, propertyName);
1481          
1482          if (orig) {
1483              CFTypeID i = CFGetTypeID(orig);
1484              if (i == CFStringGetTypeID())
1485                  value = CFStringCreateCopy(CFGetAllocator(ctxt->_properties), orig);
1486              else if (i == CFDataGetTypeID())
1487                  value = CFDataCreateCopy(CFGetAllocator(ctxt->_properties), orig);
1488              else if (i == CFDictionaryGetTypeID())
1489                  value = CFDictionaryCreateCopy(CFGetAllocator(ctxt->_properties), orig);
1490              else if (i == CFArrayGetTypeID())
1491                  value = CFArrayCreateCopy(CFGetAllocator(ctxt->_properties), orig);
1492          }
1493      }
1494  
1495      return value;
1496  }
1497  
1498  
1499  /* static */ Boolean
1500  _FTPStreamSetProperty(CFTypeRef stream, CFStringRef propertyName,
1501  					  CFTypeRef propertyValue, _CFFTPStreamContext* ctxt)
1502  {
1503      Boolean result = FALSE;
1504  	
1505      // **FIXME** Flow-changing properties should check to see if the
1506      // state machine is beyond their respective command before setting
1507      // successfully.
1508      
1509      if (CFEqual(propertyName, kCFStreamPropertyFTPProxy)) {
1510          
1511          if (!ctxt->_connection && !ctxt->_dataStream) {
1512          
1513              if (!propertyValue) {
1514                  CFDictionaryRemoveValue(ctxt->_properties, propertyName);
1515                  result = TRUE;
1516              }
1517              
1518              else if (CFGetTypeID(propertyValue) == CFDictionaryGetTypeID()) {
1519  
1520                  CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
1521                  CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
1522  				
1523  
1524  #if defined(__MACH__)
1525  				// Attempt to set the passive bit based upon proxy dictionary from SC.
1526  				CFTypeRef p = CFDictionaryGetValue(propertyValue, kSCPropNetProxiesFTPPassive);
1527  				// Only set the bit if it wasn't set explicitly and there is a value.
1528  				if (!__CFBitIsSet(ctxt->_flags, kFlagBitDidSetPassiveBit) && p) {
1529  				
1530  					// If it's a number, set it based upon zero or non-zero.
1531  					if (CFGetTypeID(p) == CFNumberGetTypeID()) {
1532  						
1533  						SInt32 val;
1534  						
1535  						CFNumberGetValue(p, kCFNumberSInt32Type, &val);
1536  						
1537  						if (val)
1538  							__CFBitSet(ctxt->_flags, kFlagBitPerformPASV);
1539  						else
1540  							__CFBitClear(ctxt->_flags, kFlagBitPerformPASV);
1541  					}
1542  					
1543  					else if (p == kCFBooleanFalse) {
1544  						__CFBitClear(ctxt->_flags, kFlagBitPerformPASV);
1545  					}
1546  				}
1547  #endif
1548  				
1549                  result = TRUE;
1550              }
1551          }
1552      }
1553      
1554      else if (CFEqual(propertyName, kCFStreamPropertySOCKSProxy)) {
1555          
1556          if (!ctxt->_connection && !ctxt->_dataStream) {
1557  
1558              if (!propertyValue) {
1559                  CFDictionaryRemoveValue(ctxt->_properties, propertyName);
1560                  result = TRUE;
1561              }
1562              
1563              else if (CFGetTypeID(propertyValue) == CFDictionaryGetTypeID()) {
1564                  
1565                  if (!CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPProxy)) {
1566  
1567                      CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertyFTPProxy);
1568                      CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
1569                      result = TRUE;
1570                  }
1571              }
1572          }
1573      }
1574      
1575      else if (CFEqual(propertyName, kCFStreamPropertyFTPUsePassiveMode)) {
1576          
1577          // Default mode (NULL or true) indicate passive use
1578          if (propertyValue && CFEqual(propertyValue, kCFBooleanFalse))
1579              __CFBitClear(ctxt->_flags, kFlagBitPerformPASV);
1580          else
1581              __CFBitSet(ctxt->_flags, kFlagBitPerformPASV);
1582              
1583  		__CFBitSet(ctxt->_flags, kFlagBitDidSetPassiveBit);
1584  
1585          result = TRUE;
1586      }
1587      
1588      else if (CFEqual(propertyName, kCFStreamPropertyFTPFetchResourceInfo)) {
1589      
1590          // Default is not to perform the STAT command
1591          if (propertyValue && CFEqual(propertyValue, kCFBooleanTrue))
1592              __CFBitSet(ctxt->_flags, kFlagBitPerformSTAT);
1593          else
1594              __CFBitClear(ctxt->_flags, kFlagBitPerformSTAT);
1595              
1596          result = TRUE;
1597      }
1598      
1599      else if (CFEqual(propertyName, kCFStreamPropertyFTPFetchNameList)) {
1600      
1601          // Default is not to perform the NLST command
1602          if (propertyValue && CFEqual(propertyValue, kCFBooleanTrue))
1603              __CFBitSet(ctxt->_flags, kFlagBitPerformNLST);
1604          else
1605              __CFBitClear(ctxt->_flags, kFlagBitPerformNLST);
1606              
1607          result = TRUE;
1608      }
1609      
1610      else if (CFEqual(propertyName, kCFStreamPropertyFTPFileTransferOffset)) {
1611      
1612          // Default is to not perform the offset (or offset to zero)
1613          if (!propertyValue)
1614              ctxt->_offset = 0;
1615          else
1616              CFNumberGetValue(propertyValue, kCFNumberLongLongType, &ctxt->_offset);
1617                      
1618          result = TRUE;
1619      }
1620      
1621      else if (CFEqual(propertyName, _kCFStreamPropertyFTPLogInOnly)) {
1622          if (propertyValue && CFEqual(propertyValue, kCFBooleanTrue))
1623              __CFBitSet(ctxt->_flags, kFlagBitLogInOnly);
1624          else
1625              __CFBitClear(ctxt->_flags, kFlagBitLogInOnly);
1626          
1627          result = TRUE;
1628      }
1629      
1630      else if (CFEqual(propertyName, _kCFStreamPropertyFTPRemoveResource)) {
1631          if (propertyValue && CFEqual(propertyValue, kCFBooleanTrue))
1632              __CFBitSet(ctxt->_flags, kFlagBitRemoveResource);
1633          else
1634              __CFBitClear(ctxt->_flags, kFlagBitRemoveResource);
1635              
1636          result = TRUE;
1637      }
1638      
1639      else if (CFEqual(propertyName, _kCFStreamPropertyFTPNewResourceName)) {
1640      
1641          CFURLRef temp = ctxt->_newUrl;
1642          ctxt->_newUrl = propertyValue ? CFURLCopyAbsoluteURL(propertyValue) : NULL;
1643          if (temp)
1644              CFRelease(temp);
1645          
1646          result = TRUE;
1647      }
1648      
1649      // kCFStreamPropertyFTPResourceSize can not be set.
1650      else if (CFEqual(propertyName, kCFStreamPropertyFTPResourceSize)) {
1651          result = FALSE;
1652      }
1653          
1654      else if (CFEqual(propertyName, kCFStreamPropertyFTPAttemptPersistentConnection)) {
1655          
1656          if (!propertyValue || !CFEqual(propertyValue, kCFBooleanFalse))
1657              CFDictionaryRemoveValue(ctxt->_properties, propertyName);
1658          else
1659              CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
1660  	result = TRUE;
1661      }
1662  
1663      else if ( CFEqual(propertyName, kCFStreamPropertyFTPUserName) || CFEqual(propertyName, kCFStreamPropertyFTPPassword) ) {
1664  
1665  	if (propertyValue != NULL) {
1666  	    // validate the propertyValue
1667  	    if ( (CFGetTypeID(propertyValue) == CFStringGetTypeID()) && _ValidFTPString(propertyValue) ) {
1668  		CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
1669  		result = TRUE;
1670  	    }
1671  	    // else the propertyValue is invalid -- don't set the username/password property and leave result as FALSE
1672  	}
1673  	else {
1674  	    CFDictionaryRemoveValue(ctxt->_properties, propertyName);
1675  	    result = TRUE;
1676  	}
1677      }
1678  
1679      else if ( CFEqual(propertyName, kCFStreamPropertyFTPUserName_prevalidated) ) {
1680  
1681  	// the propertyValue is valid, so change the propertyName and handle it
1682  	propertyName = kCFStreamPropertyFTPUserName;
1683  	if (propertyValue != NULL) {
1684  	    CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
1685  	}
1686  	else {
1687  	    CFDictionaryRemoveValue(ctxt->_properties, propertyName);
1688  	}
1689  	result = TRUE;
1690      }
1691  
1692      else if ( CFEqual(propertyName, kCFStreamPropertyFTPPassword_prevalidated) ) {
1693  
1694  	// the propertyValue is valid, so change the propertyName and handle it
1695  	propertyName = kCFStreamPropertyFTPPassword;
1696  	if (propertyValue != NULL) {
1697  	    CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
1698  	}
1699  	else {
1700  	    CFDictionaryRemoveValue(ctxt->_properties, propertyName);
1701  	}
1702  	result = TRUE;
1703      }
1704  
1705      else {
1706      
1707          if (ctxt->_dataStream) {
1708              if (CFGetTypeID(ctxt->_dataStream) == CFReadStreamGetTypeID())
1709                  result = CFReadStreamSetProperty((CFReadStreamRef)ctxt->_dataStream, propertyName, propertyValue);
1710              else
1711                  result = CFWriteStreamSetProperty((CFWriteStreamRef)ctxt->_dataStream, propertyName, propertyValue);
1712          }
1713          
1714          // **FIXME** Set the property on the control stream if there is one.  It should also
1715          // orphan the remaining queued items.
1716          
1717          if (!result) {
1718              if (propertyValue)
1719                  CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
1720              else
1721                  CFDictionaryRemoveValue(ctxt->_properties, propertyName);
1722              result = TRUE;
1723          }
1724      }
1725  
1726      return result;
1727  }
1728  
1729  
1730  /* static */ void
1731  _FTPStreamSchedule(CFTypeRef stream, CFRunLoopRef runLoop,
1732  				   CFStringRef runLoopMode, _CFFTPStreamContext* ctxt)
1733  {
1734  	if (_SchedulesAddRunLoopAndMode(ctxt->_runloops, runLoop, runLoopMode)) {
1735  
1736  		if (ctxt->_proxyStream)
1737  			CFReadStreamScheduleWithRunLoop(ctxt->_proxyStream, runLoop, runLoopMode);
1738  		
1739  		if (ctxt->_server)
1740  			_CFTypeScheduleOnRunLoop(ctxt->_server, runLoop, runLoopMode);
1741  
1742  		if (ctxt->_dataStream)
1743  			_CFTypeScheduleOnRunLoop(ctxt->_dataStream, runLoop, runLoopMode);
1744  			
1745  		if (ctxt->_connection)
1746  			_CFNetConnectionSchedule(ctxt->_connection, ctxt, runLoop, runLoopMode);
1747  	}
1748  }
1749  
1750  
1751  /* static */ void
1752  _FTPStreamUnschedule(CFTypeRef stream, CFRunLoopRef runLoop,
1753  					 CFStringRef runLoopMode, _CFFTPStreamContext* ctxt)
1754  {
1755  	if (_SchedulesRemoveRunLoopAndMode(ctxt->_runloops, runLoop, runLoopMode)) {
1756  
1757  		if (ctxt->_proxyStream)
1758  			CFReadStreamUnscheduleFromRunLoop(ctxt->_proxyStream, runLoop, runLoopMode);
1759  		
1760  		if (ctxt->_server)
1761  			_CFTypeUnscheduleFromRunLoop(ctxt->_server, runLoop, runLoopMode);
1762  
1763  		if (ctxt->_dataStream)
1764  			_CFTypeUnscheduleFromRunLoop(ctxt->_dataStream, runLoop, runLoopMode);
1765  			
1766  		if (ctxt->_connection)
1767  			_CFNetConnectionUnschedule(ctxt->_connection, ctxt, runLoop, runLoopMode);
1768      }
1769  }
1770  
1771  
1772  #if 0
1773  #pragma mark -
1774  #pragma mark CFNetConnection Callback Functions
1775  #endif
1776  
1777  
1778  /* static */ const void*
1779  _CFFTPNetConnectionContextCreate(CFAllocatorRef alloc, const _CFFTPNetConnectionContext* template) {
1780  
1781      _CFFTPNetConnectionContext* ctxt = (_CFFTPNetConnectionContext*)CFAllocatorAllocate(alloc,
1782                                                                                          sizeof(ctxt[0]),
1783                                                                                          0);
1784                                                                                            
1785      memmove(ctxt, template, sizeof(ctxt[0]));
1786      ctxt->_key = (_CFNetConnectionCacheKey)connCacheKeyRetain(alloc, template->_key);
1787  	
1788      if (!ctxt->_recvBuffer)
1789          ctxt->_recvBuffer = CFDataCreateMutable(alloc, 0);
1790          
1791      if (!ctxt->_sendBuffer)
1792          ctxt->_sendBuffer = CFDataCreateMutable(alloc, 0);
1793      __CFBitClear(ctxt->_flags, kFlagBitIsXServer);
1794      
1795      return (const void*)ctxt;
1796  }
1797  
1798  
1799  /* static */ void
1800  _CFFTPNetConnectionContextFinalize(CFAllocatorRef alloc, const _CFFTPNetConnectionContext* ctxt) {
1801      
1802      connCacheKeyRelease(alloc, ctxt->_key);
1803      
1804      if (ctxt->_root)
1805          CFRelease(ctxt->_root);
1806      
1807      if (ctxt->_recvBuffer)
1808          CFRelease(ctxt->_recvBuffer);
1809          
1810      if (ctxt->_sendBuffer)
1811          CFRelease(ctxt->_sendBuffer);
1812  
1813      CFAllocatorDeallocate(alloc, (_CFFTPNetConnectionContext*)ctxt);
1814  }
1815  
1816  
1817  /* static */ CFStreamError
1818  _FTPConnectionCreateStreams(CFAllocatorRef alloc,
1819                              const _CFFTPNetConnectionContext* ctxt,
1820                              CFWriteStreamRef* requestStream,
1821                              CFReadStreamRef* responseStream)
1822  {
1823      CFStreamError result = {0, 0};
1824      
1825      UInt32 type;
1826      SInt32 port;
1827      CFStringRef host;
1828      CFDictionaryRef properties;
1829      
1830      getValuesFromKey(ctxt->_key, &host, &port, &type, &properties);
1831  
1832      *requestStream = NULL;
1833      *responseStream = NULL;
1834  	
1835      // Get the correct host and port if using proxy.
1836      if ((type == kCFNetConnectionTypeFTPProxy) || (type == kCFNetConnectionTypeFTPSProxy)) {
1837          
1838          CFDictionaryRef proxy = CFDictionaryGetValue(properties, kCFStreamPropertyFTPProxy);
1839          CFNumberRef cfport = CFDictionaryGetValue(proxy, kCFStreamPropertyFTPProxyPort);
1840      
1841          host = CFDictionaryGetValue(proxy, kCFStreamPropertyFTPProxyHost);
1842          if (cfport)
1843              CFNumberGetValue(cfport, kCFNumberSInt32Type, &port);
1844          else if (type == kCFNetConnectionTypeFTPProxy)
1845              port = 21;
1846          else
1847              port = 990;
1848      }
1849  
1850  	_CFSocketStreamCreatePair(alloc, host, port, 0, NULL, responseStream, requestStream);
1851  
1852      if (*responseStream && *requestStream) {
1853  
1854  		CFArrayCallBacks cb = {0, NULL, NULL, NULL, NULL};
1855  		const void* values[2] = {_CFStreamSocketCreatedCallBack, NULL};
1856  		CFArrayRef callback = CFArrayCreate(alloc, values, sizeof(values) / sizeof(values[0]), &cb);
1857  		
1858  		if (callback) {
1859  			CFWriteStreamSetProperty(*requestStream, CFSTR("_kCFStreamSocketCreatedCallBack"), callback);
1860  			CFRelease(callback);
1861  		}
1862  	
1863          CFDictionaryApplyFunction(properties, (CFDictionaryApplierFunction)_StreamPropertyApplier, *responseStream);
1864          CFDictionaryApplyFunction(properties, (CFDictionaryApplierFunction)_StreamPropertyApplier, *requestStream);
1865      }
1866      else {
1867          result.domain = kCFStreamErrorDomainPOSIX;
1868          result.error = errno;
1869  #if defined(__WIN32__)
1870          if (!result.error) {
1871              result.error = WSAGetLastError();
1872              if (result.error)
1873                  result.domain = kCFStreamErrorDomainWinSock;
1874          }
1875  #endif
1876          if (!result.error)
1877              result.error = ENOMEM;
1878      }
1879  
1880      return result;
1881  }
1882  
1883  
1884  /* static */ void
1885  _FTPConnectionRequestStateChanged(_CFFTPStreamContext* ctxt, int newState,
1886                                    CFStreamError *err, _CFNetConnectionRef connection,
1887                                    _CFFTPNetConnectionContext* netCtxt)
1888  {
1889      switch (newState) {
1890  
1891          case kQueued:
1892              ctxt->_connection = connection;
1893              break;
1894          
1895          case kTransmittingRequest:
1896              {
1897                  CFArrayRef a = ctxt->_runloops;
1898                  int i, count = CFArrayGetCount(a);
1899                  
1900                  for (i = 0; i < count; i += 2) {
1901                      _CFNetConnectionSchedule(connection,
1902                                               ctxt,
1903                                               (CFRunLoopRef)CFArrayGetValueAtIndex(a, i),
1904                                               (CFStringRef)CFArrayGetValueAtIndex(a, i + 1));
1905                  }
1906                  
1907                  if (netCtxt->_state == kFTPStateIdle)
1908                      _StartProcess(netCtxt, ctxt);
1909                  else if (netCtxt->_state > kFTPStateIdle) {
1910                      __CFBitSet(netCtxt->_flags, kFlagBitReturnToIdle);
1911                      _CFNetConnectionGetState(ctxt->_connection, TRUE, ctxt);
1912                  }
1913              }
1914              break;
1915              
1916          case kFinished:
1917  //        case kCancelled:
1918              {
1919                  CFBooleanRef persistent = (CFBooleanRef)CFDictionaryGetValue(ctxt->_properties,
1920                                                                               kCFStreamPropertyFTPAttemptPersistentConnection);
1921                  Boolean usePersistent = (persistent && CFEqual(persistent, kCFBooleanFalse)) ? FALSE : TRUE;
1922                  __CFBitSet(netCtxt->_flags, kFlagBitLeftForDead);
1923                  if (usePersistent)
1924                      CFDictionarySetValue(gFTPConnectionTimeouts, ctxt->_connection, CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + kFTPTimeoutInSeconds));
1925              }
1926              // NOTE that this falls through to kOrphaned on purpose.
1927              
1928          case kOrphaned:
1929              {
1930                  CFArrayRef a = ctxt->_runloops;
1931                  int i, count = CFArrayGetCount(a);
1932  
1933                  _CFNetConnectionDequeue(connection, ctxt);
1934                  
1935                  for (i = 0; i < count; i += 2) {
1936                      _CFNetConnectionUnschedule(connection,
1937                                                 ctxt,
1938                                                 (CFRunLoopRef)CFArrayGetValueAtIndex(a, i),
1939                                                 (CFStringRef)CFArrayGetValueAtIndex(a, i + 1));
1940                  }
1941                  
1942                  CFRelease(ctxt->_connection);
1943                  ctxt->_connection = NULL;
1944              
1945                  if ((newState == kOrphaned) && !__CFBitIsSet(ctxt->_flags, kFlagBitGotError)) {
1946                      
1947                      CFStreamError error;
1948                      Boolean open;
1949                      
1950                      _FTPStreamOpen(ctxt->_userStream, &error, &open, ctxt);
1951                      
1952                      if (open) {
1953                          
1954                          CFStreamEventType event = kCFStreamEventErrorOccurred;
1955                          if (!error.error)
1956                              event = kCFStreamEventOpenCompleted;
1957  
1958                          if (CFGetTypeID(ctxt->_userStream) == CFReadStreamGetTypeID())
1959                              CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, event, &error);
1960                          else
1961                              CFWriteStreamSignalEvent((CFWriteStreamRef)ctxt->_userStream, event, &error);
1962                      }
1963                  }
1964              }
1965              
1966              break;
1967                  
1968          default:
1969              break;
1970      }
1971  }
1972  
1973  
1974  /* static */ void
1975  _FTPConnectionTransmitRequest(_CFFTPStreamContext* ctxt, _CFNetConnectionRef connection, _CFFTPNetConnectionContext* netCtxt) {
1976  
1977      CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(connection);
1978  	CFReadStreamRef rStream = _CFNetConnectionGetResponseStream(connection);
1979  	
1980      if (CFWriteStreamCanAcceptBytes(wStream)) {
1981          if (__CFBitIsSet(netCtxt->_flags, kFlagBitHTTPLitmus))
1982              _FTPRequestStreamCallBack(ctxt, wStream, kCFStreamEventCanAcceptBytes, connection, netCtxt);
1983          else {
1984              CFStringRef cmd;
1985              CFStringRef user = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPUserName);
1986  
1987              CFStringRef host;
1988              SInt32 port;
1989              UInt32 type;
1990              CFDictionaryRef properties;
1991  
1992              CFAllocatorRef alloc = CFGetAllocator(ctxt->_runloops);
1993  
1994              getValuesFromKey(netCtxt->_key, &host, &port, &type, &properties);
1995  
1996              if (user)
1997                  CFRetain(user);
1998              else {
1999                  user = CFURLCopyUserName(ctxt->_url);
2000                  if (!user)
2001                      user = CFRetain(kAnonymousUserString);
2002              }
2003  
2004              if ((type == kCFNetConnectionTypeFTPProxy) || (type == kCFNetConnectionTypeFTPSProxy)) {
2005  
2006                  CFStringRef newUser;
2007  
2008                  // **FIXME** This may not be kosher.  Is the URL passed into the proxy
2009                  // allowed to have a ':' and port appended to it?
2010                  if (((type == kCFNetConnectionTypeFTPProxy) && (port == 21)) ||
2011                      ((type == kCFNetConnectionTypeFTPSProxy) && (port == 990)))
2012                  {
2013                      newUser = CFStringCreateWithFormat(alloc, NULL, kFTPProxyFormat, user, host);
2014                  }
2015                  else
2016                      newUser = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@@%@:%ld"), user, host, port);
2017  
2018                  CFRelease(user);
2019                  user = newUser;
2020              }
2021  
2022              cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPUSERCommandString, user);
2023  
2024              CFRelease(user);
2025              __CFBitSet(netCtxt->_flags, kFlagBitHTTPLitmus);	// NOTE that this is set before the call to WriteCommand
2026      
2027              if (cmd) {
2028                  _WriteCommand(netCtxt, ctxt, cmd);
2029                  CFRelease(cmd);
2030              }
2031              else {
2032                  CFStreamError error = {kCFStreamErrorDomainPOSIX, errno};
2033  
2034                  if (!error.error)
2035                      error.error = ENOMEM;
2036                  _ReportError(ctxt, &error);
2037              }
2038          }
2039      }
2040  	
2041      if (ctxt->_connection && CFReadStreamHasBytesAvailable(rStream))
2042  		_FTPConnectionReceiveResponse(ctxt, connection, netCtxt);
2043  }
2044  
2045  
2046  /* static */ void
2047  _FTPConnectionReceiveResponse(_CFFTPStreamContext* ctxt, _CFNetConnectionRef connection, _CFFTPNetConnectionContext* netCtxt) {
2048  
2049      CFReadStreamRef rStream = _CFNetConnectionGetResponseStream(connection);
2050      CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(connection);
2051  
2052      if (CFReadStreamHasBytesAvailable(rStream))
2053              _FTPResponseStreamCallBack(ctxt, rStream, kCFStreamEventHasBytesAvailable, connection, netCtxt);
2054  	
2055      if (ctxt->_connection && CFWriteStreamCanAcceptBytes(wStream))
2056  		_FTPConnectionTransmitRequest(ctxt, connection, netCtxt);
2057  }
2058  
2059  
2060  /* static */ void
2061  _FTPResponseStreamCallBack(_CFFTPStreamContext* ctxt, CFReadStreamRef stream,
2062                             CFStreamEventType type, _CFNetConnectionRef conn,
2063                             _CFFTPNetConnectionContext* netCtxt)
2064  {
2065      switch (type) {
2066  
2067          case kCFStreamEventHasBytesAvailable:
2068              {
2069                  CFIndex i, canRead = CFDataGetLength(netCtxt->_recvBuffer) - netCtxt->_recvCount;
2070                  
2071                  // **FIXME** There are false positives coming through in
2072                  // heavily threaded tests.
2073                  if (!CFReadStreamHasBytesAvailable(stream))
2074                      return;
2075                  
2076                  if (canRead < kBufferGrowthSize) {
2077                      CFDataSetLength(netCtxt->_recvBuffer, netCtxt->_recvCount + kBufferGrowthSize);
2078                      if (CFDataGetLength(netCtxt->_recvBuffer) < (netCtxt->_recvCount + kBufferGrowthSize)) {
2079                          CFStreamError error = {kCFStreamErrorDomainPOSIX, ENOMEM};
2080                          _ReportError(ctxt, &error);
2081                          return;
2082                      }
2083                      canRead = kBufferGrowthSize;
2084                  }
2085  
2086                  i = CFReadStreamRead(stream,
2087                                       CFDataGetMutableBytePtr(netCtxt->_recvBuffer) + netCtxt->_recvCount,
2088                                       canRead);
2089  
2090                  if (i < 0)
2091                      _FTPResponseStreamCallBack(ctxt, stream, kCFStreamEventErrorOccurred, conn, netCtxt);
2092                  else if (!i) {
2093                      if (__CFBitIsSet(netCtxt->_flags, kFlagBitLeftForDead)) {
2094  
2095                          CFStreamError error;
2096                          Boolean openComplete;
2097  
2098                          _CFNetConnectionLost(ctxt->_connection);
2099                          _CFNetConnectionDequeue(ctxt->_connection, ctxt);
2100  						
2101  						CFRelease(ctxt->_connection);
2102  						ctxt->_connection = NULL;
2103  						
2104                          _FTPStreamOpen(ctxt->_userStream, &error, &openComplete, ctxt);
2105                      }
2106                      else
2107                          _FTPResponseStreamCallBack(ctxt, stream, kCFStreamEventEndEncountered, conn, netCtxt);
2108                  }
2109                  else {						
2110  
2111                      netCtxt->_recvCount += i;
2112  
2113                      _HandleResponse(netCtxt, ctxt);
2114                  }
2115              }
2116              break;
2117  
2118          case kCFStreamEventErrorOccurred:
2119              {
2120                  CFStreamError error = CFReadStreamGetError(stream);
2121                  _ReportError(ctxt, &error);
2122              }
2123              break;
2124  
2125          case kCFStreamEventEndEncountered:
2126              {
2127                  // **FIXME** Deal with cases where an end is okay.
2128                  CFStreamError error = {_kCFStreamErrorDomainNativeSockets, ENOTCONN};
2129                  _ReportError(ctxt, &error);
2130              }
2131              break;
2132  			
2133          default:
2134              break;
2135      }
2136  }
2137  
2138  
2139  /* static */ void
2140  _FTPRequestStreamCallBack(_CFFTPStreamContext* ctxt, CFWriteStreamRef stream,
2141                            CFStreamEventType type, _CFNetConnectionRef conn,
2142                            _CFFTPNetConnectionContext* netCtxt)
2143  {
2144      switch (type) {
2145  
2146          case kCFStreamEventCanAcceptBytes:
2147              {
2148                  if (!__CFBitIsSet(netCtxt->_flags, kFlagBitHTTPLitmus)) {
2149                      _FTPConnectionTransmitRequest(ctxt, conn, netCtxt);		// NOTE that this is a re-entrant call
2150                      break;
2151                  }
2152  
2153                  if (netCtxt->_sendCount) {
2154                      UInt8* buffer = CFDataGetMutableBytePtr(netCtxt->_sendBuffer);
2155                      CFIndex i = CFWriteStreamWrite(stream, buffer, netCtxt->_sendCount);
2156  
2157                      if (i < 0)
2158                          _FTPRequestStreamCallBack(ctxt, stream, kCFStreamEventErrorOccurred, conn, netCtxt);
2159                      else if (!i)
2160                          _FTPRequestStreamCallBack(ctxt, stream, kCFStreamEventEndEncountered, conn, netCtxt);
2161                      else {
2162                          netCtxt->_sendCount -= i;
2163                          memmove(buffer, buffer + i, netCtxt->_sendCount);
2164                      }
2165                  }
2166              }
2167              break;
2168  
2169          case kCFStreamEventErrorOccurred:
2170              {
2171                  CFStreamError error = CFWriteStreamGetError(stream);
2172                  _ReportError(ctxt, &error);
2173              }
2174              break;
2175  
2176          case kCFStreamEventEndEncountered:
2177              {
2178                  // **FIXME** Deal with cases where an end is okay.
2179                  CFStreamError error = {_kCFStreamErrorDomainNativeSockets, ENOTCONN};
2180                  _ReportError(ctxt, &error);
2181              }
2182              break;
2183  			
2184          default:
2185              break;
2186      }
2187  }
2188  
2189  /* static */ CFArrayRef
2190  _FTPRunLoopArrayCallBack(_CFFTPStreamContext *ctxt, _CFNetConnectionRef conn, _CFFTPNetConnectionContext *netCtxt) {
2191      return ctxt->_runloops;
2192  }
2193  
2194  
2195  #if 0
2196  #pragma mark -
2197  #pragma mark Utility Functions
2198  #endif
2199  
2200  /* static */ Boolean
2201  _IsRoot(CFURLRef url) {
2202      Boolean isAbsolute;  
2203      CFStringRef strictPath, resourceSpecifier;
2204      strictPath = CFURLCopyStrictPath(url, &isAbsolute);
2205      resourceSpecifier = CFURLCopyResourceSpecifier(url);
2206      if (!strictPath && !resourceSpecifier)  return TRUE;
2207      if (strictPath) CFRelease(strictPath);
2208      if (resourceSpecifier) CFRelease(resourceSpecifier);
2209      return FALSE;     
2210  }
2211  
2212  /* static */ void
2213  _FTPConnectionCacheCreate(void) {
2214  	
2215  	if (!_kFTPConnectionCallBacks) {
2216  		
2217  		_kFTPConnectionCallBacks = (_CFNetConnectionCallBacks*)CFAllocatorAllocate(kCFAllocatorDefault, sizeof(_kFTPConnectionCallBacks[0]), 0);
2218  		
2219  		assert(_kFTPConnectionCallBacks != NULL);
2220  		
2221  		_kFTPConnectionCallBacks->version = 0;
2222  		_kFTPConnectionCallBacks->create = (const void* (*)(CFAllocatorRef, const void*))_CFFTPNetConnectionContextCreate;
2223  		_kFTPConnectionCallBacks->finalize = (void (*)(CFAllocatorRef, const void*))_CFFTPNetConnectionContextFinalize;
2224  		_kFTPConnectionCallBacks->createConnectionStreams = (CFStreamError (*)(CFAllocatorRef, const void*, CFWriteStreamRef*, CFReadStreamRef*))_FTPConnectionCreateStreams;
2225  		_kFTPConnectionCallBacks->requestStateChanged = (void (*)(void*, int, CFStreamError*, _CFNetConnectionRef, const void*))_FTPConnectionRequestStateChanged;
2226  		_kFTPConnectionCallBacks->transmitRequest = (void (*)(void*, _CFNetConnectionRef, const void*))_FTPConnectionTransmitRequest;
2227  		_kFTPConnectionCallBacks->receiveResponse = (void (*)(void*, _CFNetConnectionRef, const void*))_FTPConnectionReceiveResponse;
2228  		_kFTPConnectionCallBacks->responseStreamCallBack = (void (*)(void*, CFReadStreamRef, CFStreamEventType, _CFNetConnectionRef, const void*))_FTPResponseStreamCallBack;
2229  		_kFTPConnectionCallBacks->requestStreamCallBack = (void (*)(void*, CFWriteStreamRef, CFStreamEventType, _CFNetConnectionRef, const void*))_FTPRequestStreamCallBack;
2230  		_kFTPConnectionCallBacks->runLoopAndModesArrayForRequest = (CFArrayRef (*)(void *, _CFNetConnectionRef, const void*))_FTPRunLoopArrayCallBack;
2231  	}
2232  	
2233      gFTPConnectionTimeouts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2234      gFTPConnectionCache = createConnectionCache();
2235  }
2236  
2237  /* static */ void
2238  _FTPConnectionCacheExpiration(_CFNetConnectionRef conn, CFDateRef expiration, CFMutableArrayRef list) {
2239  
2240      if (CFAbsoluteTimeGetCurrent() >= CFDateGetAbsoluteTime(expiration))
2241          CFArrayAppendValue(list, conn);
2242  }
2243  
2244  
2245  /* static */ void
2246  _SetSOCKS4ProxyInformation(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt, CFURLRef proxyUrl) {
2247  
2248      CFStringRef pHost = CFURLCopyHostName(proxyUrl);
2249      SInt32 p = CFURLGetPortNumber(proxyUrl);
2250      CFNumberRef pPort = CFNumberCreate(alloc, kCFNumberSInt32Type, &p);
2251      CFStringRef pUser = CFURLCopyUserName(proxyUrl);
2252      CFStringRef pPass = CFURLCopyPassword(proxyUrl);
2253      CFMutableDictionaryRef pInfo = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2254  
2255      CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSProxyHost, pHost);
2256      CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSProxyPort, pPort);
2257  
2258      CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSVersion, kCFStreamSocketSOCKSVersion4);
2259  
2260      if (pUser) {
2261          CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSUser, pUser);
2262          CFRelease(pUser);
2263      }
2264  
2265      if (pPass) {
2266          CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSPassword, pPass);
2267          CFRelease(pPass);
2268      }
2269  
2270      CFDictionaryAddValue(ctxt->_properties, kCFStreamPropertySOCKSProxy, pInfo);
2271      CFRelease(pInfo);
2272  
2273      CFRelease(pHost);
2274      CFRelease(pPort);
2275  }
2276  
2277  
2278  /* static */ void
2279  _SetSOCKS5ProxyInformation(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt, CFURLRef proxyUrl) {
2280  
2281      CFStringRef pHost = CFURLCopyHostName(proxyUrl);
2282      SInt32 p = CFURLGetPortNumber(proxyUrl);
2283      CFNumberRef pPort = CFNumberCreate(alloc, kCFNumberSInt32Type, &p);
2284      CFStringRef pUser = CFURLCopyUserName(proxyUrl);
2285      CFStringRef pPass = CFURLCopyPassword(proxyUrl);
2286      CFMutableDictionaryRef pInfo = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2287  
2288      CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSProxyHost, pHost);
2289      CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSProxyPort, pPort);
2290  
2291      if (pUser) {
2292          CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSUser, pUser);
2293          CFRelease(pUser);
2294      }
2295  
2296      if (pPass) {
2297          CFDictionaryAddValue(pInfo, kCFStreamPropertySOCKSPassword, pPass);
2298          CFRelease(pPass);
2299      }
2300  
2301      CFDictionaryAddValue(ctxt->_properties, kCFStreamPropertySOCKSProxy, pInfo);
2302      CFRelease(pInfo);
2303  
2304      CFRelease(pHost);
2305      CFRelease(pPort);
2306  }
2307  
2308  
2309  /* static */ void
2310  _StartHTTPRequest(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt, CFStreamError* error, CFURLRef proxyUrl) {
2311  	
2312      CFURLRef url;
2313      CFHTTPMessageRef msg;
2314      CFMutableDictionaryRef pInfo;
2315      CFURLComponentsRFC1808 comps;
2316      CFStreamClientContext streamCtxt = {0, ctxt, NULL, NULL, NULL};
2317      CFStringRef user = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPUserName);
2318      CFStringRef pass = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPPassword);
2319      CFHTTPMessageRef resp = (CFHTTPMessageRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyFTPLastHTTPResponse);
2320      
2321      __CFBitSet(ctxt->_flags, kFlagBitIsHTTPRequest);
2322  
2323      memset(error, 0, sizeof(error[0]));
2324  
2325      if (user)
2326          CFRetain(user);
2327      else
2328          user = CFURLCopyUserName(ctxt->_url);
2329  
2330      if (pass)
2331          CFRetain(pass);
2332      else
2333          pass = CFURLCopyPassword(ctxt->_url);
2334  
2335  
2336      // **FIXME** Create new URL to use.  This should use the new range API's instead.
2337      // Remove #include "CFPriv.h" when switched.
2338      memset(&comps, 0, sizeof(comps));
2339      _CFURLCopyComponents(ctxt->_url, kCFURLComponentDecompositionRFC1808, &comps);
2340  
2341      if (!comps.user)
2342          comps.user = user ? CFURLCreateStringByAddingPercentEscapes(alloc, user, NULL, NULL, kCFStringEncodingUTF8) : NULL;
2343  
2344      if (!comps.password)
2345          comps.password = pass ? CFURLCreateStringByAddingPercentEscapes(alloc, pass, NULL, NULL, kCFStringEncodingUTF8) : NULL;
2346  
2347      if (comps.query) {
2348          CFRelease(comps.query);
2349          comps.query = NULL;
2350      }
2351  
2352      if (comps.fragment) {
2353          CFRelease(comps.fragment);
2354          comps.fragment = NULL;
2355      }
2356  
2357      if (comps.parameterString) {
2358          CFRelease(comps.parameterString);
2359          comps.parameterString = NULL;
2360      }
2361  
2362      if (user)
2363          CFRelease(user);
2364  
2365      if (pass)
2366          CFRelease(pass);
2367  
2368      url = _CFURLCreateFromComponents(alloc, kCFURLComponentDecompositionRFC1808, &comps);
2369  
2370      if (comps.scheme)
2371          CFRelease(comps.scheme);
2372      if (comps.user)
2373          CFRelease(comps.user);
2374      if (comps.password)
2375          CFRelease(comps.password);
2376      if (comps.host)
2377          CFRelease(comps.host);
2378      if (comps.pathComponents)
2379          CFRelease(comps.pathComponents);
2380      if (comps.baseURL)
2381          CFRelease(comps.baseURL);
2382  
2383      msg = CFHTTPMessageCreateRequest(alloc, kHTTPGETMethod, url ? url : ctxt->_url, kCFHTTPVersion1_1);
2384  
2385      if (url)
2386          CFRelease(url);
2387  
2388      if (resp) {
2389          CFHTTPMessageAddAuthentication(msg,
2390                                         resp,
2391                                         CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPProxyUser),
2392                                         CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPProxyPassword),
2393                                         NULL,
2394                                         TRUE);
2395      }
2396  
2397      ctxt->_dataStream = CFReadStreamCreateForHTTPRequest(alloc, msg);
2398      CFRelease(msg);
2399      
2400      if (!ctxt->_dataStream) {
2401          error->error = errno;
2402          error->domain = kCFStreamErrorDomainPOSIX;
2403  #if defined(__WIN32__)
2404          if (!error->error) {
2405              error->error = WSAGetLastError();
2406              if (error->error)
2407                  error->domain = kCFStreamErrorDomainWinSock;
2408          }
2409  #endif
2410          if (!error->error)
2411              error->error = ENOMEM;
2412      }
2413      else {
2414          CFStringRef pScheme = CFURLCopyScheme(proxyUrl);
2415          CFStringRef pHost = CFURLCopyHostName(proxyUrl);
2416          SInt32 p = CFURLGetPortNumber(proxyUrl);
2417          CFNumberRef pPort = CFNumberCreate(alloc, kCFNumberSInt32Type, &p);
2418          CFBooleanRef persistent = (CFBooleanRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPAttemptPersistentConnection);
2419          
2420          pInfo = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2421  
2422          if (CFStringCompare(pScheme, kHTTPSchemeString, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
2423              CFDictionarySetValue(pInfo, kCFStreamPropertyHTTPProxyHost, pHost);
2424              CFDictionarySetValue(pInfo, kCFStreamPropertyHTTPProxyPort, pPort);
2425          }
2426          else {
2427              CFDictionarySetValue(pInfo, kCFStreamPropertyHTTPSProxyHost, pHost);
2428              CFDictionarySetValue(pInfo, kCFStreamPropertyHTTPSProxyPort, pPort);
2429          }
2430          CFReadStreamSetProperty((CFReadStreamRef)ctxt->_dataStream, kCFStreamPropertyHTTPProxy, pInfo);
2431          CFRelease(pInfo);
2432          
2433          if (!persistent || CFEqual(persistent, kCFBooleanTrue))
2434              CFReadStreamSetProperty((CFReadStreamRef)ctxt->_dataStream, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue);
2435          
2436          CFDictionaryApplyFunction(ctxt->_properties, (CFDictionaryApplierFunction)_StreamPropertyApplier, (void*)ctxt->_dataStream);
2437  
2438          CFReadStreamSetClient((CFReadStreamRef)ctxt->_dataStream, ~0L, (CFReadStreamClientCallBack)_DataStreamCallBack, &streamCtxt);
2439  
2440  		_CFTypeScheduleOnMultipleRunLoops(ctxt->_dataStream,ctxt->_runloops);
2441  
2442          CFReadStreamOpen((CFReadStreamRef)ctxt->_dataStream);
2443  
2444          CFRelease(pHost);
2445          CFRelease(pPort);
2446          CFRelease(pScheme);
2447      }
2448  }
2449  
2450  
2451  /* static */ Boolean
2452  _ProcessHTTPResponse(_CFFTPStreamContext* ctxt, CFStreamError* error) {
2453  
2454      UInt32 code;
2455      CFHTTPMessageRef resp = (CFHTTPMessageRef)CFReadStreamCopyProperty((CFReadStreamRef)ctxt->_dataStream, kCFStreamPropertyHTTPResponseHeader);
2456  
2457      memset(error, 0, sizeof(error[0]));
2458      
2459      if (!resp)
2460          return FALSE;
2461  
2462      code = CFHTTPMessageGetResponseStatusCode(resp);
2463      
2464      if (code < 300)
2465          __CFBitSet(ctxt->_flags, kFlagBitReadHTTPResponse);
2466      
2467      else if (code == 407 && !__CFBitIsSet(ctxt->_flags, kFlagBit407TriedOnce) &&
2468               CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPProxyUser) &&
2469               CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyFTPProxyPassword))
2470      {
2471          Boolean openComplete = FALSE;
2472  
2473          __CFBitSet(ctxt->_flags, kFlagBit407TriedOnce);
2474  
2475          _ReleaseDataReadStream(ctxt);
2476  
2477          CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertyFTPLastHTTPResponse, resp);
2478          _FTPStreamOpen(ctxt->_userStream, error, &openComplete, ctxt);
2479          CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertyFTPLastHTTPResponse);
2480      }
2481      else {
2482          error->domain = kCFStreamErrorDomainFTP;
2483          error->error = code;
2484      }
2485      
2486      CFRelease(resp);
2487  
2488      return TRUE;
2489  }
2490  
2491  
2492  /* static */ void
2493  _RollOverHTTPRequest(_CFFTPStreamContext* ctxt, CFStreamError* error) {
2494      
2495      Boolean openComplete = FALSE;
2496  
2497      _ReleaseDataReadStream(ctxt);
2498      
2499      ctxt->_current++;
2500      ctxt->_error = *error;
2501      __CFBitClear(ctxt->_flags, kFlagBit407TriedOnce);
2502  
2503      _FTPStreamOpen(ctxt->_userStream, error, &openComplete, ctxt);
2504  }
2505  
2506  
2507  /* static */ void
2508  _CFStreamSocketCreatedCallBack(int fd, void* ctxt) {
2509  
2510  	int yes = 1;
2511  	
2512  	(void)ctxt;		/* unused */
2513  	
2514  	setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(yes));
2515  }
2516  
2517  
2518  /* static */ void
2519  _DataStreamCallBack(CFTypeRef stream, CFStreamEventType type, _CFFTPStreamContext* ctxt) {
2520  
2521      if (__CFBitIsSet(ctxt->_flags, kFlagBitIsHTTPRequest) || type != kCFStreamEventEndEncountered) {
2522          
2523          CFStreamError error;
2524          CFTypeID i = CFReadStreamGetTypeID();
2525          
2526          if (CFGetTypeID(stream) == i)
2527              error = CFReadStreamGetError((CFReadStreamRef)stream);
2528          else 
2529              error = CFWriteStreamGetError((CFWriteStreamRef)stream);
2530  
2531          if (__CFBitIsSet(ctxt->_flags, kFlagBitIsHTTPRequest) && !__CFBitIsSet(ctxt->_flags, kFlagBitReadHTTPResponse))
2532          {
2533              if (type == kCFStreamEventHasBytesAvailable) {
2534                  if (_ProcessHTTPResponse(ctxt, &error)) {
2535  
2536                      if (error.error)
2537                          type = kCFStreamEventErrorOccurred;
2538                      else if (__CFBitIsSet(ctxt->_flags, kFlagBit407TriedOnce))
2539                          return;		// Do not signal the event since having to retry.
2540                  }
2541                  else if (ctxt->_current < CFArrayGetCount(ctxt->_proxies)) {
2542  
2543                      _RollOverHTTPRequest(ctxt, &error);
2544  
2545                      if (error.error)
2546                          type = kCFStreamEventErrorOccurred;
2547                      else
2548                          return;		// Do not signal the event since having to retry.
2549                  }
2550              }
2551              else if ((type == kCFStreamEventErrorOccurred) && (ctxt->_current < CFArrayGetCount(ctxt->_proxies))) {
2552  
2553                  _RollOverHTTPRequest(ctxt, &error);
2554  
2555                  if (error.error)
2556                      type = kCFStreamEventErrorOccurred;
2557                  else
2558                      return;		// Do not signal the event since having to retry.
2559              }
2560          }
2561  
2562          if (CFGetTypeID(ctxt->_userStream) == i)
2563              CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, type, &error);
2564          else
2565              CFWriteStreamSignalEvent((CFWriteStreamRef)ctxt->_userStream, type, &error);
2566      }
2567  }
2568  
2569  
2570  /* static */ void
2571  _ReleaseDataReadStream(_CFFTPStreamContext* ctxt) {
2572  
2573      CFArrayRef a = ctxt->_runloops;
2574      CFReadStreamRef s = (CFReadStreamRef)ctxt->_dataStream;
2575  
2576      CFReadStreamSetClient(s, 0, NULL, NULL);
2577  	
2578  	_CFTypeUnscheduleFromMultipleRunLoops(s, a);
2579  
2580      CFReadStreamClose(s);
2581      CFRelease(s);
2582      ctxt->_dataStream = NULL;
2583  }
2584  
2585  
2586  /* static */ void
2587  _SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address,
2588                  const void *data, _CFFTPStreamContext* ctxt)
2589  {
2590      CFStreamClientContext streamCtxt = {0, ctxt, NULL, NULL, NULL};
2591      CFAllocatorRef alloc = CFGetAllocator(ctxt->_properties);
2592      
2593      if (!data || (*((CFSocketNativeHandle*)data) == -1)) {
2594          CFStreamError error = {_kCFStreamErrorDomainNativeSockets, *((int*)data)};
2595          _ReportError(ctxt, &error);
2596      }
2597      
2598      if (type != kCFSocketAcceptCallBack)
2599          return;
2600      
2601      if (__CFBitIsSet(ctxt->_flags, kFlagBitPerformUpload))
2602  		_CFSocketStreamCreatePair(alloc, NULL, 0, *((CFSocketNativeHandle*)data), NULL, NULL, (CFWriteStreamRef*)&ctxt->_dataStream);
2603      else
2604  		_CFSocketStreamCreatePair(alloc, NULL, 0, *((CFSocketNativeHandle*)data), NULL, (CFReadStreamRef*)&ctxt->_dataStream, NULL);
2605      
2606      CFDictionaryApplyFunction(ctxt->_properties, (CFDictionaryApplierFunction)_StreamPropertyApplier, (void*)ctxt->_dataStream);
2607      
2608      if (CFGetTypeID(ctxt->_dataStream) == CFReadStreamGetTypeID()) {
2609      
2610          CFReadStreamSetClient((CFReadStreamRef)ctxt->_dataStream, ~0L, (CFReadStreamClientCallBack)_DataStreamCallBack, &streamCtxt);
2611          
2612  		_CFTypeScheduleOnMultipleRunLoops(ctxt->_dataStream, ctxt->_runloops);
2613          
2614          CFReadStreamOpen((CFReadStreamRef)ctxt->_dataStream);
2615      }
2616      else {
2617      
2618          CFWriteStreamSetClient((CFWriteStreamRef)ctxt->_dataStream, ~0L, (CFWriteStreamClientCallBack)_DataStreamCallBack, &streamCtxt);
2619          
2620  		_CFTypeScheduleOnMultipleRunLoops(ctxt->_dataStream, ctxt->_runloops);
2621          
2622          CFWriteStreamOpen((CFWriteStreamRef)ctxt->_dataStream);
2623      }
2624      
2625      _InvalidateServer(ctxt);
2626  }
2627  
2628  
2629  /* static */ void
2630  _StreamPropertyApplier(CFTypeRef key, CFTypeRef value, CFTypeRef stream) {
2631      
2632      if (CFGetTypeID(stream) == CFReadStreamGetTypeID())
2633          CFReadStreamSetProperty((CFReadStreamRef)stream, key, value);
2634      else
2635          CFWriteStreamSetProperty((CFWriteStreamRef)stream, key, value);
2636  }
2637  
2638  
2639  /* static */ void
2640  _ReportError(_CFFTPStreamContext* ctxt, CFStreamError* error) {
2641  
2642      if (ctxt->_connection &&
2643          (((_CFFTPNetConnectionContext*)_CFNetConnectionGetInfoPointer(ctxt->_connection))->_state == kFTPStateConnect) &&
2644          (ctxt->_current < CFArrayGetCount(ctxt->_proxies)))
2645      {
2646          ctxt->_current++;
2647          ctxt->_error = *error;
2648          _CFNetConnectionErrorOccurred(ctxt->_connection, error);
2649          return;
2650      }
2651  
2652      __CFBitSet(ctxt->_flags, kFlagBitGotError);
2653  
2654      if (ctxt->_connection)
2655          _CFNetConnectionErrorOccurred(ctxt->_connection, error);
2656      
2657      if (ctxt->_dataStream) {
2658  		
2659          if (CFGetTypeID(ctxt->_dataStream) == CFReadStreamGetTypeID())
2660              _ReleaseDataReadStream(ctxt);
2661  		
2662          else {
2663  			
2664  			_CFTypeInvalidate(ctxt->_dataStream);
2665  			_CFTypeUnscheduleFromMultipleRunLoops(ctxt->_dataStream, ctxt->_runloops);
2666              
2667              CFWriteStreamClose((CFWriteStreamRef)(ctxt->_dataStream));
2668              CFRelease(ctxt->_dataStream);
2669              ctxt->_dataStream = NULL;
2670          }
2671      }
2672  	
2673      if (CFGetTypeID(ctxt->_userStream) == CFReadStreamGetTypeID())
2674          CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, error);
2675      else
2676          CFWriteStreamSignalEvent((CFWriteStreamRef)ctxt->_userStream, kCFStreamEventErrorOccurred, error);
2677  }
2678  
2679  
2680  /* static */ void
2681  _ConnectionComplete(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
2682  
2683      _CFFTPStreamState old_state = ctxt->_state;
2684      CFArrayRef a = ftpCtxt->_runloops;
2685      int i, count = CFArrayGetCount(a);
2686      for (i = 0; i < count; i += 2) {
2687          _CFNetConnectionUnschedule(ftpCtxt->_connection,
2688                                     ftpCtxt,
2689                                     (CFRunLoopRef)CFArrayGetValueAtIndex(a, i),
2690                                     (CFStringRef)CFArrayGetValueAtIndex(a, i + 1));
2691      }
2692      
2693      ctxt->_state = kFTPStateIdle;
2694      
2695      _CFNetConnectionRequestIsComplete(ftpCtxt->_connection, ftpCtxt);
2696  
2697         // was this a RETR command?
2698         if (old_state == kFTPStateRETR) {
2699                 // has the _dataStream completed yet?
2700                 if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitCompleteDeferred)) {
2701                         // yes, so the response is complete
2702      _CFNetConnectionResponseIsComplete(ftpCtxt->_connection, ftpCtxt);
2703                         __CFBitClear(ftpCtxt->_flags, kFlagBitCompleteDeferred);
2704                 }
2705                 else {
2706                         // no, so set the flag and wait for the _dataStream to complete
2707                         __CFBitSet(ftpCtxt->_flags, kFlagBitCompleteDeferred);
2708                 }
2709         }
2710         else {
2711                 _CFNetConnectionResponseIsComplete(ftpCtxt->_connection, ftpCtxt);
2712                 __CFBitClear(ftpCtxt->_flags, kFlagBitCompleteDeferred);
2713         }
2714  
2715      // 3266164 Let the end event come from the data stream at this point.  It's
2716      // now properly unwound from the connection, so let things progress naturally.
2717      if (!ftpCtxt->_server && !ftpCtxt->_dataStream) {
2718          if (CFGetTypeID(ftpCtxt->_userStream) == CFReadStreamGetTypeID())
2719              CFReadStreamSignalEvent((CFReadStreamRef)ftpCtxt->_userStream, kCFStreamEventEndEncountered, NULL);
2720          else
2721              CFWriteStreamSignalEvent((CFWriteStreamRef)ftpCtxt->_userStream, kCFStreamEventEndEncountered, NULL);
2722      }
2723  }
2724  
2725  
2726  /* static */ Boolean
2727  _PASVAddressParser(const UInt8* buffer, struct sockaddr_in* saddr)
2728  {
2729      const UInt8* walk = buffer + 3;
2730      int byteCount = 0;
2731      u_long	host = 0;
2732      u_short	port = 0;
2733      memset(saddr, 0, sizeof(saddr[0]));
2734  
2735      while (*walk) {
2736          if (isdigit(*walk)) {
2737              unsigned	temp;
2738  
2739              sscanf((const char*)walk, "%ud", &temp);
2740              // WARNING: "clever" code follows..
2741              if (byteCount < 4) {
2742                  host |= (temp << (24 - (8 * byteCount)));
2743              } else {
2744                  port |= (temp << (8 - (8 * (byteCount - 4))));
2745              }
2746  
2747              // Break out when all the bytes have been retrieved.
2748              if (byteCount < 5) {
2749                  byteCount ++;
2750              } else {
2751                  host = ntohl(host);
2752                  memmove(&saddr->sin_addr, &host, sizeof(u_long));
2753  #if !defined(__WIN32__)
2754                  saddr->sin_len = sizeof(saddr[0]);
2755  #endif
2756                  saddr->sin_family = AF_INET;
2757                  saddr->sin_port = ntohs(port);
2758                  return TRUE;
2759              }
2760  
2761              // step past the digit to get the next one.
2762              while (isdigit(*walk))
2763                  walk++;
2764          }
2765          walk ++;
2766      }
2767      
2768      return FALSE;
2769  }
2770  
2771  
2772  /* static */ Boolean
2773  _EPSVPortParser(const UInt8* buffer, struct sockaddr_in6* saddr)
2774  {
2775      const UInt8* walk = buffer + 3;
2776      while (*walk) {
2777          if (!isdigit(*walk)) walk++;
2778          else {
2779              unsigned tmp;
2780              sscanf((const char*)walk, "%ud", &tmp);
2781              saddr->sin6_port = htons((tmp & 0x0000FFFF));
2782              return TRUE;
2783          }
2784      }
2785      return FALSE;
2786  }
2787  
2788  
2789  /* static */ u_char
2790  _GetProtocolFamily(_CFFTPStreamContext* ctxt, UInt8* buffer)
2791  {
2792      CFDataRef native = NULL;
2793      socklen_t addrlen = SOCK_MAXADDRLEN;
2794      struct sockaddr* addr = (struct sockaddr*)&(buffer[0]);
2795      u_char result = 255;
2796      
2797      CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(ctxt->_connection);
2798      native = CFWriteStreamCopyProperty(wStream, kCFStreamPropertySocketNativeHandle);
2799      if (native) {
2800          memset(buffer, 0, addrlen);
2801          if (!getpeername(*((int*)CFDataGetBytePtr(native)), addr, &addrlen))
2802              result = addr->sa_family;
2803          CFRelease(native);
2804      }
2805      return result;
2806  }
2807  
2808  
2809  /* static */ Boolean
2810  _CreateListenerForContext(CFAllocatorRef alloc, _CFFTPStreamContext* ctxt) {
2811  
2812      CFDataRef native = NULL;
2813      CFDataRef address = NULL;
2814      CFRunLoopSourceRef src = NULL;
2815      
2816      do {
2817          int yes = 1;
2818          UInt8 buffer[SOCK_MAXADDRLEN];
2819          socklen_t addrlen = sizeof(buffer);
2820          struct sockaddr* addr = (struct sockaddr*)&(buffer[0]);
2821          struct sockaddr_in* addr4 = (struct sockaddr_in*)&(buffer[0]);
2822          struct sockaddr_in6* addr6 = (struct sockaddr_in6*)&(buffer[0]);
2823          CFSocketContext socketCtxt = {0, ctxt, NULL, NULL, NULL};
2824      
2825          CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(ctxt->_connection);
2826      
2827          native = CFWriteStreamCopyProperty(wStream, kCFStreamPropertySocketNativeHandle);
2828          
2829          if (!native)
2830              break;
2831      
2832          memset(buffer, 0, sizeof(buffer));
2833              
2834          if (getsockname(*((int*)CFDataGetBytePtr(native)), addr, &addrlen))
2835              break;
2836      
2837          CFRelease(native);
2838          native = NULL;
2839  
2840          ctxt->_server = CFSocketCreate(alloc,
2841                                         addr->sa_family,
2842                                         SOCK_STREAM,
2843                                         IPPROTO_TCP,
2844                                         kCFSocketAcceptCallBack,
2845                                         (CFSocketCallBack)&_SocketCallBack,
2846                                         &socketCtxt);
2847                                                  
2848          if (!ctxt->_server)
2849              break;
2850              
2851          setsockopt(CFSocketGetNative(ctxt->_server), SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes));
2852              
2853          if (addr->sa_family == AF_INET)
2854              addr4->sin_port = 0;
2855          else
2856              addr6->sin6_port = 0;
2857  
2858          // Wrap the native address structure for CFSocketCreate.
2859          address = CFDataCreateWithBytesNoCopy(alloc, (const UInt8*)addr, (addr->sa_family == AF_INET) ? sizeof(addr4[0]) : sizeof(addr6[0]), kCFAllocatorNull);
2860          
2861          if (!address)
2862              break;
2863  			
2864          // Set the local binding which causes the socket to start listening.
2865          if (CFSocketSetAddress(ctxt->_server, address) != kCFSocketSuccess)
2866              break;
2867  
2868          CFRelease(address);
2869          address = NULL;
2870  
2871          if (!ctxt->_runloops)
2872              return TRUE;
2873              
2874          src = CFSocketCreateRunLoopSource(alloc, ctxt->_server, 0);
2875          if (!src)
2876              break;
2877          
2878  		_CFTypeScheduleOnMultipleRunLoops(src, ctxt->_runloops);
2879  
2880          CFRelease(src);
2881  
2882          return TRUE;
2883  
2884      } while (0);
2885      
2886      if (native)
2887          CFRelease(native);
2888          
2889      if (address)
2890          CFRelease(address);
2891          
2892      if (src)
2893          CFRelease(src);
2894          
2895      _InvalidateServer(ctxt);
2896      
2897      return FALSE;
2898  }
2899  
2900  
2901  /* static */ void
2902  _StartTransfer(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
2903  
2904      CFStringRef cmd, target = CFURLCopyLastPathComponent(ftpCtxt->_url);
2905      CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
2906  	
2907      if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformUpload)) {
2908          ctxt->_state = kFTPStateSTOR;
2909          cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPSTORCommandString, target);
2910  
2911          if (ftpCtxt->_dataStream)
2912              CFWriteStreamOpen((CFWriteStreamRef)ftpCtxt->_dataStream);
2913      }
2914  
2915      else {
2916          // Handle the retrieval of a directory listing
2917          if (CFURLHasDirectoryPath(ftpCtxt->_url) || _IsRoot(ftpCtxt->_url)) {
2918              if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformNLST)) {
2919                  cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPNLSTCommandString, target);
2920                  ctxt->_state = kFTPStateNLST;
2921  
2922                  if (ftpCtxt->_dataStream)
2923                      CFReadStreamOpen((CFReadStreamRef)ftpCtxt->_dataStream);
2924              }
2925              else {
2926                  cmd = CFRetain(kCFFTPLISTCommandString);
2927                  ctxt->_state = kFTPStateLIST;
2928  
2929                  if (ftpCtxt->_dataStream)
2930                      CFReadStreamOpen((CFReadStreamRef)ftpCtxt->_dataStream);
2931              }
2932          }
2933  
2934          // Handle the retrieval of a file
2935          else {
2936  
2937              if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformSTAT)) {
2938                  ctxt->_state = kFTPStateSIZE;
2939                  cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPSIZECommandString, target);
2940              }
2941              else {
2942                  if (!ftpCtxt->_offset) {
2943                      cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRETRCommandString, target);
2944                      ctxt->_state = kFTPStateRETR;
2945                                         __CFBitClear(ftpCtxt->_flags, kFlagBitCompleteDeferred);
2946  
2947                      if (ftpCtxt->_dataStream)
2948                          CFReadStreamOpen((CFReadStreamRef)ftpCtxt->_dataStream);
2949                  }
2950                  else {
2951                      cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRESTCommandString, ftpCtxt->_offset);
2952                      ctxt->_state = kFTPStateREST;
2953                  }
2954              }
2955          }
2956      }
2957  
2958      if (target) CFRelease(target);
2959      
2960      _WriteCommand(ctxt, ftpCtxt, cmd);
2961      CFRelease(cmd);
2962  }
2963  
2964  
2965  /* static */ void
2966  _InvalidateServer(_CFFTPStreamContext* ctxt) {
2967          
2968      if (!ctxt->_server)
2969          return;
2970      
2971      if (ctxt->_runloops) {    
2972  		_CFTypeUnscheduleFromMultipleRunLoops(ctxt->_server, ctxt->_runloops);
2973      }
2974      
2975      CFSocketInvalidate(ctxt->_server);
2976      CFRelease(ctxt->_server);
2977      
2978      ctxt->_server = NULL;
2979  }
2980  
2981  
2982  /* static */ CFStringRef
2983  _CreatePathForContext(CFAllocatorRef alloc, _CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
2984  
2985      CFStringRef path = CFURLCopyFileSystemPath(ftpCtxt->_url, kCFURLPOSIXPathStyle);
2986      if (!CFStringGetLength(path)) {
2987          CFRelease(path);
2988          path = CFRetain(kCFFTPRootPathString);
2989      }
2990  
2991      // 3619570 path prefixed with "//" indicates root of "/".  This mimics
2992      // Jaguar and IE behavior.
2993  	if (CFStringHasPrefix(path, kCFFTPForcedRootPathPrefix)) {
2994  		CFStringRef temp = CFStringCreateWithSubstring(alloc, path, CFRangeMake(1, CFStringGetLength(path) - 1));
2995  		if (temp) {
2996  			CFRelease(path);
2997  			path = temp;
2998  		}
2999  	}
3000  	
3001      else if (ctxt->_root) {
3002          CFStringRef temp = CFStringCreateWithFormat(alloc, NULL, kCFFTPPathFormatString, ctxt->_root, path);
3003          if (temp) {
3004              CFRelease(path);
3005              path = temp;
3006          }
3007      }
3008      
3009      return path;
3010  }
3011  
3012  
3013  /* static */ void
3014  _WriteCommand(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, CFStringRef cmd) {
3015  	
3016      UInt8* buffer;
3017      CFIndex dlen = CFDataGetLength(ctxt->_sendBuffer);
3018      CFIndex slen = CFStringGetLength(cmd);
3019      CFIndex req = CFStringGetBytes(cmd, CFRangeMake(0, slen), kCFStringEncodingMacRoman, '_', FALSE, NULL, 0, NULL);
3020      
3021      CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(ftpCtxt->_connection);
3022  	
3023      if ((dlen - ctxt->_sendCount) < req)
3024          CFDataSetLength(ctxt->_sendBuffer, dlen + req - (dlen - ctxt->_sendCount));
3025  	
3026      buffer = CFDataGetMutableBytePtr(ctxt->_sendBuffer);
3027      
3028      CFStringGetBytes(cmd, CFRangeMake(0, slen), kCFStringEncodingMacRoman, '_', FALSE, buffer + ctxt->_sendCount, req, NULL);
3029      
3030      ctxt->_sendCount += req;
3031      
3032      if (ctxt->_sendCount && CFWriteStreamCanAcceptBytes(wStream)) {
3033          _FTPRequestStreamCallBack(ftpCtxt,
3034                                    wStream,
3035                                    kCFStreamEventCanAcceptBytes,
3036                                    ftpCtxt->_connection,
3037                                    ctxt);
3038      }
3039  }
3040  
3041  
3042  /* static */ void
3043  _HandleResponse(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
3044  
3045      CFIndex count = ctxt->_recvCount;
3046  
3047      while (count >= 4) {
3048  
3049          UInt32 result = -1;
3050          UInt8* buffer = (UInt8*)CFDataGetMutableBytePtr(ctxt->_recvBuffer);
3051          Boolean isMultiline = FALSE;
3052          
3053          UInt8* newline = memchr(buffer, '\n', count);
3054          if (!newline) break;
3055  
3056  		if (isdigit(buffer[0]) && isdigit(buffer[1]) && isdigit(buffer[2])) {
3057  			
3058              result = ((buffer[0] - '0') * 100) + ((buffer[1] - '0') * 10) + (buffer[2] - '0');
3059  			
3060  			if (buffer[3] == '-')
3061  				isMultiline = TRUE;
3062  			
3063  			else if (buffer[3] != ' ')
3064  				result = -1;
3065  		}
3066              
3067          if (__CFBitIsSet(ctxt->_flags, kFlagBitMultiline)) {
3068                  
3069              if ((result == ctxt->_result) && !isMultiline)
3070                  __CFBitClear(ctxt->_flags, kFlagBitMultiline);
3071          }
3072          else {
3073  
3074              if (*(newline - 1) != '\r') break;
3075              
3076              ctxt->_result = result;
3077              
3078              if (!(ctxt->_result > 99) && (ctxt->_result < 600)) {
3079              
3080                  CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
3081                  _ReportError(ftpCtxt, &error);
3082                  break;
3083              }
3084                  
3085              if (isMultiline)
3086                  __CFBitSet(ctxt->_flags, kFlagBitMultiline);
3087              else
3088                  __CFBitClear(ctxt->_flags, kFlagBitMultiline);
3089          }
3090  		
3091  		_AdvanceStateMachine(ctxt, ftpCtxt, buffer, newline - buffer, __CFBitIsSet(ctxt->_flags, kFlagBitMultiline));
3092  
3093          newline++;
3094  
3095          count -= (newline - buffer);
3096          memmove(buffer, newline, count);
3097          ctxt->_recvCount = count;
3098      }
3099  }
3100  
3101  
3102  /*
3103      _ValidFTPString validates an FTP <string>. rfc959 defines <string> as:
3104         <string> ::= <char> | <char><string>
3105         <char> ::= any of the 128 ASCII characters except <CR> and <LF>
3106      (the 128 ASCII are 7-bit ASCII)
3107  */
3108  /* static */ Boolean
3109  _ValidFTPString(CFStringRef theString) {
3110      Boolean result;
3111      CFIndex length;
3112      CFIndex idx;
3113      CFStringInlineBuffer buf;
3114  
3115      result = TRUE;  // default result
3116  
3117      length = CFStringGetLength(theString);
3118      CFStringInitInlineBuffer(theString, &buf, CFRangeMake(0, length));
3119      for ( idx = 0; idx < length; ++idx ) {
3120  	UniChar uChar;
3121  
3122  	uChar = CFStringGetCharacterFromInlineBuffer(&buf, idx);
3123  	if ( (uChar == 0x000a) || (uChar == 0x000d) ) {
3124  	    result = FALSE;
3125  	    break;
3126  	}
3127      }
3128  
3129      return ( result );
3130  }
3131  
3132  
3133  /* static */ CFURLRef
3134  _ConvertToCFFTPHappyURL(CFURLRef url) {
3135  
3136      CFURLRef result = NULL;
3137      UInt8 stack_buffer[2048];
3138      UInt8* buffer = &stack_buffer[0];
3139      CFURLRef tmp = CFURLCopyAbsoluteURL(url);
3140      CFIndex length;
3141  
3142      if (!tmp)
3143          return NULL;
3144      url = tmp;
3145      
3146      length = CFURLGetBytes(url, buffer, sizeof(stack_buffer));
3147  
3148      if (-1 == length) {
3149  
3150          CFIndex req = CFURLGetBytes(url, NULL, 0);
3151          buffer = (UInt8*)malloc(req);
3152          if (!buffer) {
3153  	    CFRelease(tmp);
3154              return NULL;
3155  	}
3156  
3157          length = CFURLGetBytes(url, buffer, req);
3158      }
3159  
3160      result = CFURLCreateAbsoluteURLWithBytes(CFGetAllocator(url), buffer, length, kCFStringEncodingMacRoman, NULL, FALSE);
3161  
3162      if (buffer != &stack_buffer[0])
3163          free(buffer);
3164  
3165  	CFRelease(tmp);
3166  
3167      return result;
3168  }
3169  
3170  
3171  /* static */ Boolean
3172  _ReadModeBits(const UInt8* str, int* mode) {
3173  
3174      // Function returns TRUE if the string appears to be
3175      // recognizable mode bits.  Returns false otherwise.
3176      Boolean result = TRUE;
3177      int i;
3178  	
3179  	for (i = 0; result && (i < 9); i += 3) {
3180  		
3181  		if (str[i] == 'r') *mode |= (1 << (8 - i));		// See if it's readable
3182  		else if (str[i] != '-') result = FALSE;			// Make sure it's another valid character then
3183  		
3184  		if (str[i + 1] == 'w') *mode |= (1 << (7 - i));		// See if it's writable
3185  		else if (str[i + 1] != '-') result = FALSE;			// Make sure it's another valid character then
3186  		
3187  		switch (str[i + 2]) {
3188  			
3189  			case 'x':
3190  				*mode |= (1 << (6 - i));
3191  				break;
3192  				
3193  			case 's':
3194  				*mode |= (1 << (6 - i));
3195  				if ((i / 3) == 0)
3196  					*mode |= 04000;
3197  				else if ((i / 3) == 1)
3198  					*mode |= 02000;
3199  				break;
3200  				
3201  			case 't':
3202  				*mode |= (1 << (6 - i));
3203  				if ((i / 3) == 2)
3204  					*mode |= 01000;
3205  				break;
3206  				
3207  			case '-':
3208  				break;
3209  				
3210  			case 'S':
3211  				if ((i / 3) == 0)
3212  					*mode |= 04000;
3213  				else if ((i / 3) == 1)
3214  					*mode |= 02000;
3215  				break;
3216  				
3217  			case 'T':
3218  				if ((i / 3) == 2)
3219  					*mode |= 01000;
3220  				break;
3221  				
3222  			default:
3223  				result = FALSE;
3224  				break;				// Any other character is invalid
3225  		}
3226  	}
3227  
3228      return result;
3229  }
3230  
3231  
3232  /* static */ Boolean
3233  _ReadSize(const UInt8* str, UInt64* size) {
3234  
3235      const UInt8* iter = str;
3236  
3237      *size = 0;
3238  
3239      if (!isdigit(*iter))
3240          return FALSE;
3241  
3242      while (isdigit(*iter))
3243          iter++;
3244      
3245      if (!isspace(*iter))
3246          return FALSE;
3247  
3248  #if defined(__WIN32__)
3249      *size = _atoi64(str);
3250  #else
3251      *size = strtoull((const char*)str, NULL, 10);
3252  #endif
3253  
3254      return TRUE;
3255  }
3256  
3257  
3258  /* static */ CFStringRef
3259  _CFStringCreateCopyWithStrippedHTML(CFAllocatorRef alloc, CFStringRef theString) {
3260  	
3261  	CFStringRef result = NULL;
3262  	
3263  	/* If it doesn't smell like html, return the retained argument. */
3264  	if (!CFStringHasPrefix(theString, kHTMLTagOpen) || !CFStringHasSuffix(theString, kHTMLTagClose))
3265  		result = CFStringCreateCopy(alloc, theString);
3266  
3267  	/* Looks like it might be html. */
3268  	else {
3269  		
3270  		/* Look forward for the close of the opening tag. */
3271  		CFRange r1 = CFStringFind(theString, kHTMLTagClose, 0);
3272  		
3273  		/* Look backward for the open of the closing tag. */
3274  		CFRange r2 = CFStringFind(theString, kHTMLTagOpen, kCFCompareBackwards);
3275  		
3276  		/* If there are no bytes between the two, just return the retained argument. */
3277  		if (r1.location >= r2.location)
3278  			result = CFStringCreateCopy(alloc, theString);
3279  		
3280  		/* Create a copy of the bytes between the two tags. */
3281  		else {
3282  			r1.length = (r2.location - r1.location) - 1;
3283  			r1.location += 1;
3284  			result = CFStringCreateWithSubstring(alloc, theString, r1);
3285  		}
3286  	}
3287  
3288  	return result;
3289  }
3290  
3291  
3292  /* static */ const UInt8*
3293  _CFFTPGetDateTimeFunc(CFAllocatorRef alloc, const UInt8* str, CFIndex length, CFDateRef* date) {
3294  
3295      static const char kMonthStrs[12][3] = {
3296  		{'J', 'a', 'n'},
3297  		{'F', 'e', 'b'},
3298  		{'M', 'a', 'r'},
3299  		{'A', 'p', 'r'},
3300  		{'M', 'a', 'y'},
3301  		{'J', 'u', 'n'},
3302  		{'J', 'u', 'l'},
3303  		{'A', 'u', 'g'},
3304  		{'S', 'e', 'p'},
3305  		{'O', 'c', 't'},
3306  		{'N', 'o', 'v'},
3307  		{'D', 'e', 'c'}
3308  	};
3309  
3310      CFIndex i;
3311      Boolean hourIsYear = TRUE;
3312      SInt8 month, day, minute = 0;
3313      SInt32 hour = 0;
3314  
3315      *date = NULL;
3316  
3317      if (length < 9)
3318          return NULL;
3319      
3320      for (month = 0; month < (sizeof(kMonthStrs) / sizeof(kMonthStrs[0])); month++) {
3321  
3322          if (!memcmp(str, kMonthStrs[month], 3)) {
3323              break;
3324          }
3325      }
3326  
3327      if ((month == (sizeof(kMonthStrs) / sizeof(kMonthStrs[0]))) || !isspace(str[3]))
3328          return NULL;
3329  
3330      month++;
3331      
3332      i = 4;
3333      while ((i < length) && !isdigit(str[i]))
3334          i++;
3335  
3336      if (i == length)
3337          return NULL;
3338  
3339      day = (str[i++] - '0');
3340      if (i == length)
3341          return NULL;
3342  
3343      if (isdigit(str[i])) {
3344          day *= 10;
3345          day += (str[i++] - '0');
3346      }
3347          
3348      if ((i == length) || !isspace(str[i]))
3349          return NULL;
3350  
3351      while ((i < length) && !isdigit(str[i]))
3352          i++;
3353  
3354      while ((i < length) && isdigit(str[i])) {
3355          hour *= 10;
3356          hour += (str[i++] - '0');
3357      }
3358  
3359      if ((i < length) && (str[i] == ':')) {
3360          
3361          hourIsYear = FALSE;
3362          
3363          i++;
3364          
3365          while ((i < length) && isdigit(str[i])) {
3366              minute *= 10;
3367              minute += (str[i++] - '0');
3368          }
3369      }
3370  
3371      if ((i == length) || isspace(str[i])) {
3372          
3373  		CFTimeZoneRef tz = CFTimeZoneCopyDefault();
3374          CFAbsoluteTime t = CFAbsoluteTimeGetCurrent();
3375          CFGregorianDate d = CFAbsoluteTimeGetGregorianDate(t, tz);
3376          
3377          if (hourIsYear) {
3378              
3379              if (hour < 100)
3380                  d.year = 1900 + hour;
3381              else
3382                  d.year = hour;
3383              
3384              d.hour = 0;
3385              d.minute = 0;
3386              d.second = 0;
3387  			
3388  			d.month = month;
3389  			d.day = day;
3390          }
3391          
3392          else {
3393              CFAbsoluteTime t2;
3394  			CFGregorianDate d2 = CFAbsoluteTimeGetGregorianDate((t + 86400.0), tz);
3395  			
3396              d.hour = hour & 0xFF;
3397              d.minute = minute;
3398              d.second = 0;
3399  			
3400  			d.month = month;
3401  			d.day = day;
3402  			d.year = d2.year;
3403  			
3404  			t2 = CFGregorianDateGetAbsoluteTime(d, tz);
3405  			if (t2 > (t + 86400.0))
3406  				d.year--;
3407          }
3408  		
3409          t = CFGregorianDateGetAbsoluteTime(d, tz);
3410  		if (tz)
3411  			CFRelease(tz);
3412          *date = CFDateCreate(alloc, t);
3413  
3414          return &str[i];
3415      }
3416  
3417      return NULL;
3418  }
3419  
3420  #if defined(PROXY_PAC_SUPPORT)
3421  
3422  /* static */ void
3423  _ProxyStreamCallBack(CFReadStreamRef proxyStream, _CFFTPStreamContext* ctxt) {
3424  
3425      Boolean complete = FALSE;
3426      ctxt->_proxies = _CFNetworkCopyProxyFromProxyStream(ctxt->_proxyStream, &complete);
3427      if (complete) {
3428  
3429          CFStreamError error = CFReadStreamGetError(ctxt->_proxyStream);
3430  	
3431  		_CFTypeUnscheduleFromMultipleRunLoops(ctxt->_proxyStream, ctxt->_runloops);
3432          
3433          CFRelease(ctxt->_proxyStream);
3434          ctxt->_proxyStream = NULL;
3435  
3436          if (ctxt->_proxies) {
3437  
3438              Boolean open;
3439      
3440              _FTPStreamOpen(ctxt->_userStream, &error, &open, ctxt);
3441      
3442              if (open) {
3443      
3444                  CFStreamEventType event = kCFStreamEventErrorOccurred;
3445                  if (!error.error)
3446                      event = kCFStreamEventOpenCompleted;
3447      
3448                  if (CFGetTypeID(ctxt->_userStream) == CFReadStreamGetTypeID())
3449                      CFReadStreamSignalEvent((CFReadStreamRef)ctxt->_userStream, event, &error);
3450                  else
3451                      CFWriteStreamSignalEvent((CFWriteStreamRef)ctxt->_userStream, event, &error);
3452              }
3453          }
3454          
3455          else {
3456              if (!error.error) {
3457                  error.domain = kCFStreamErrorDomainPOSIX;
3458                  error.error = errno;
3459                  if (!error.error)
3460                      error.error = EIO;
3461              }
3462              _ReportError(ctxt, &error);
3463          }
3464      }
3465  }
3466  
3467  #endif /* PROXY_PAC_SUPPORT */
3468  
3469  #if defined(__WIN32__)
3470  extern void
3471  _CFFTPCleanup(void) {
3472  
3473      __CFSpinLock(&gFTPSpinLock);
3474      if (gFTPConnectionCache != NULL) {
3475          releaseConnectionCache(gFTPConnectionCache);
3476          gFTPConnectionCache = NULL;
3477      }
3478      __CFSpinUnlock(&gFTPSpinLock);
3479  }
3480  #endif
3481  
3482  
3483  /* static */ CFIndex
3484  _FindLine(const UInt8 *buffer, CFIndex bufferLength, const UInt8** start, const UInt8** end) {
3485  /*
3486   * This function finds lines delimited on either side by CR or LF characters.
3487  */
3488  	CFIndex consumed;
3489  	
3490  	*start = NULL;
3491  	*end = NULL;
3492  	consumed = 0;
3493  	
3494  	if ( (buffer != NULL) && (bufferLength != 0) ) {
3495  		const UInt8* lastBufChar;
3496  		const UInt8* startOfLine;
3497  		
3498  		lastBufChar = buffer + bufferLength - 1;
3499  		
3500  		/* find the start of the line... the first non CR or LF character */
3501  		startOfLine = buffer;
3502  		while ( startOfLine <= lastBufChar ) {
3503  			if ( *startOfLine != '\r' && *startOfLine != '\n' ) {
3504  				break;
3505  			}
3506  			++startOfLine;
3507  		}
3508  		
3509  		/* if there characters left, see if there's a line */
3510  		if ( startOfLine <= lastBufChar ) {
3511  			const UInt8* endOfLine;
3512  			const UInt8* firstend = NULL;
3513  			
3514  			/* find the end of the line... the character before the next CR or LF character (if any) */
3515  			endOfLine = startOfLine;
3516  			while ( endOfLine <= lastBufChar ) {
3517  				if ( *endOfLine == '\r' || *endOfLine == '\n' ) {
3518  					break;
3519  				}
3520  				++endOfLine;
3521  			}
3522  			firstend = endOfLine;
3523  			
3524  			/* if endOfLine is still within buffer, we have a line */
3525  			if ( endOfLine <= lastBufChar ) {
3526  				const UInt8* lastend;
3527  				
3528  				/* return the first and last characters of the line */
3529  				*start = startOfLine;
3530  				*end = endOfLine;
3531  				
3532  				/* find the last CR or LF character after the line */
3533  				lastend = firstend;
3534  				while ( lastend <= lastBufChar ) {
3535  					if ( *lastend != '\r' && *lastend != '\n' ) {
3536  						break;
3537  					}
3538  					++lastend;
3539  				}
3540  				
3541  				/* consume everthing up through the last CR or LF character */
3542  				consumed = lastend - buffer;
3543  			}
3544  			else {
3545  				/* no line -- just consume the CR and LF characters at the beginning of the buffer */
3546  				consumed = startOfLine - buffer;
3547  			}
3548  		}
3549  		else {
3550  			/* the buffer is all CR or LF characters -- consume them */
3551  			consumed = bufferLength;
3552  		}
3553  	}
3554  	
3555  	return ( consumed );
3556  }
3557  
3558  
3559  #if 0
3560  #pragma mark -
3561  #pragma mark State Machine
3562  #endif
3563  
3564  /* static */ void
3565  _AdvanceStateMachine(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length, Boolean isMultiLine) {
3566  
3567      if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
3568          
3569          if (ctxt->_state <= kFTPStateIdle)
3570              __CFBitClear(ctxt->_flags, kFlagBitReturnToIdle);
3571          else if (ctxt->_state < kFTPStateRETR) {
3572              if (!isMultiLine) {
3573                  ctxt->_state = kFTPStateIdle;
3574                  _StartProcess(ctxt, ftpCtxt);
3575              }
3576              return;
3577          }
3578      }
3579  
3580      switch (ctxt->_state) {
3581  
3582          case kFTPStateConnect:
3583              if (!isMultiLine) _HandleConnect(ctxt, ftpCtxt, line, length);
3584              break;
3585  
3586          case kFTPStateUSER:
3587              if (!isMultiLine) _HandleUsername(ctxt, ftpCtxt);
3588              break;
3589  
3590          case kFTPStatePASS:
3591              if (!isMultiLine) _HandlePassword(ctxt, ftpCtxt);
3592              break;
3593  			
3594          case kFTPStateSYST:
3595              if (!isMultiLine) _HandleSystem(ctxt, ftpCtxt, line, length);
3596              break;
3597              
3598          case kFTPStateSITEDIRSTYLE:
3599              if (!isMultiLine) _HandleSiteDirStyle(ctxt, ftpCtxt, line, length);
3600              break;
3601              
3602          case kFTPStateSITETRUTH:
3603              if (!isMultiLine) _HandleSiteTruth(ctxt, ftpCtxt);
3604              break;
3605              
3606          case kFTPStatePWD:
3607              if (!isMultiLine) _HandlePrintWorkingDirectory(ctxt, ftpCtxt, line, length);
3608              break;
3609  
3610          case kFTPStateTYPE:
3611              if (!isMultiLine) _HandleType(ctxt, ftpCtxt);
3612              break;
3613  
3614          case kFTPStatePASV:
3615              if (!isMultiLine) _HandlePassive(ctxt, ftpCtxt, line, length);
3616              break;
3617  
3618          case kFTPStatePORT:
3619              if (!isMultiLine) _HandlePort(ctxt, ftpCtxt);
3620              break;
3621  			
3622          case kFTPStateSIZE:
3623              if (!isMultiLine) _HandleSize(ctxt, ftpCtxt, line, length);
3624              break;
3625  			
3626          case kFTPStateSTAT:
3627              _HandleStat(ctxt, ftpCtxt, line, length, isMultiLine);
3628              break;
3629              
3630          case kFTPStateREST:
3631              if (!isMultiLine) _HandleRestart(ctxt, ftpCtxt);
3632              break;
3633  
3634          case kFTPStateRETR:
3635              if (!isMultiLine) _HandleRetrieve(ctxt, ftpCtxt);
3636              break;
3637  		
3638          case kFTPStateNLST:
3639              if (!isMultiLine) _HandleNameList(ctxt, ftpCtxt);
3640              break;
3641  
3642          case kFTPStateCWD:
3643              if (!isMultiLine) _HandleChangeDirectory(ctxt, ftpCtxt);
3644              break;
3645  
3646          case kFTPStateLIST:
3647              if (!isMultiLine) _HandleList(ctxt, ftpCtxt);
3648              break;
3649              
3650          case kFTPStateSTOR:
3651              if (!isMultiLine) _HandleStore(ctxt, ftpCtxt);
3652              break;
3653              
3654          case kFTPStateMKD:
3655              if (!isMultiLine) _HandleMakeDirectory(ctxt, ftpCtxt);
3656              break;
3657              
3658          case kFTPStateRMD:
3659              if (!isMultiLine) _HandleRemoveDirectory(ctxt, ftpCtxt);
3660              break;
3661              
3662          case kFTPStateDELE:
3663              if (!isMultiLine) _HandleDelete(ctxt, ftpCtxt);
3664              break;
3665              
3666          case kFTPStateRNFR:
3667              if (!isMultiLine) _HandleRenameFrom(ctxt, ftpCtxt);
3668              break;
3669              
3670          case kFTPStateRNTO:
3671              if (!isMultiLine) _HandleRenameTo(ctxt, ftpCtxt);
3672              break;
3673  
3674          default:
3675              break;
3676      }
3677  }
3678  
3679  
3680  /* static */ void
3681  _HandleConnect(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length) {
3682  
3683      // Valid returns for connect are:
3684      //		120, 220, 421
3685  
3686      if (ctxt->_result < 200)
3687          return;
3688              
3689      else if (ctxt->_result >= 300) {
3690              
3691          CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
3692          _ReportError(ftpCtxt, &error);
3693      }
3694      else {
3695          
3696          CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_runloops);
3697          
3698          CFStringRef system = CFStringCreateWithBytes(alloc,
3699                                                       line,
3700                                                       length,
3701                                                       kCFStringEncodingUTF8,
3702                                                       FALSE);
3703  		
3704  		if (!system) {
3705  			system = CFStringCreateWithBytes(alloc,
3706  											 line,
3707  											 length,
3708  											 kCFStringEncodingISOLatin1,
3709  											 FALSE);
3710  		}
3711  		
3712  		if (system) {
3713  		
3714  			CFRange range = CFRangeMake(0, CFStringGetLength(system));
3715  			
3716  			if (CFStringFindWithOptions(system, kCFFTPOSXSystemString, range, 0, NULL))
3717  				__CFBitSet(ctxt->_flags, kFlagBitIsXServer);
3718  			
3719  			CFRelease(system);
3720  		}
3721          
3722          ctxt->_state = kFTPStateUSER;
3723      }
3724  }
3725  
3726  
3727  /* static */ void
3728  _HandleUsername(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
3729  
3730      // Valid returns for USER are:
3731      //		230, 331, 332, 421, 500, 501, 530
3732  
3733      if ((ctxt->_result < 200) || (ctxt->_result >= 400)) {
3734          CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
3735          _ReportError(ftpCtxt, &error);
3736      }
3737      
3738      else if (ctxt->_result >= 300) {
3739  
3740          CFStringRef cmd;
3741          CFStringRef pass = CFDictionaryGetValue(ftpCtxt->_properties, kCFStreamPropertyFTPPassword);
3742  
3743          if (pass)
3744              CFRetain(pass);
3745          else {
3746              pass = CFURLCopyPassword(ftpCtxt->_url);
3747              if (!pass)
3748                  pass = CFRetain(kAnonymousPasswordString);
3749          }
3750  
3751          cmd = CFStringCreateWithFormat(CFGetAllocator(ftpCtxt->_runloops), NULL, kCFFTPPASSCommandString, pass);
3752  
3753          CFRelease(pass);
3754  
3755          if (cmd) {
3756              ctxt->_state = kFTPStatePASS;
3757              _WriteCommand(ctxt, ftpCtxt, cmd);
3758              CFRelease(cmd);
3759          }
3760          else {
3761              CFStreamError error = {kCFStreamErrorDomainPOSIX, errno};
3762  
3763              if (!error.error)
3764                  error.error = ENOMEM;
3765              _ReportError(ftpCtxt, &error);
3766          }
3767      }
3768  
3769      else {
3770          ctxt->_state = kFTPStateSYST;
3771          _WriteCommand(ctxt, ftpCtxt, kCFFTPSYSTCommandString);
3772      }
3773  }
3774  
3775  
3776  /* static */ void
3777  _HandlePassword(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
3778  
3779      // Valid returns for PASS are:
3780      //		230, 202, 332, 421, 500, 501, 503, 530
3781  
3782      if ((ctxt->_result < 200) || (ctxt->_result >= 400)) {
3783          CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
3784          _ReportError(ftpCtxt, &error);
3785      }
3786      
3787      else if (ctxt->_result >= 300) {
3788          // **FIXME** This is not a true failure case but an account
3789          // is required which is not supported yet.  This would require
3790          // an issue of the ACCT command.
3791          CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
3792          _ReportError(ftpCtxt, &error);
3793      }
3794      
3795      else {
3796          ctxt->_state = kFTPStateSYST;
3797          _WriteCommand(ctxt, ftpCtxt, kCFFTPSYSTCommandString);
3798      }
3799  }
3800  
3801  
3802  /* static */  void
3803  _HandleSystem(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length) {
3804  
3805      // Valid returns for SYST are:
3806      //		215, 421, 500, 501, 502
3807  
3808      CFStringRef system = NULL;
3809      CFRange range;
3810  
3811      if ((ctxt->_result >= 200) && (ctxt->_result < 300)) {
3812          system = CFStringCreateWithBytes(CFGetAllocator(ftpCtxt->_properties),
3813                                           line,
3814                                           length,
3815                                           kCFStringEncodingUTF8,
3816                                           FALSE);
3817          range = CFRangeMake(0, CFStringGetLength(system));
3818      }
3819      
3820      if (system && CFStringFindWithOptions(system, kCFFTPWindowsNTSystemString, range, 0, NULL)) {
3821          ctxt->_state = kFTPStateSITEDIRSTYLE;
3822          _WriteCommand(ctxt, ftpCtxt, kCFFTPSITEDIRSTYLECommandString);
3823      }
3824      else if( __CFBitIsSet(ctxt->_flags, kFlagBitIsXServer)) {
3825          ctxt->_state = kFTPStateSITETRUTH;
3826          _WriteCommand(ctxt, ftpCtxt, kCFFTPSITETRUTHCommandString);
3827      } else { 
3828          ctxt->_state = kFTPStatePWD; 
3829          _WriteCommand(ctxt, ftpCtxt, kCFFTPPWDCommandString);
3830      }
3831  
3832      if (system)
3833          CFRelease(system);
3834  }
3835  
3836  
3837  /* static */ void
3838  _HandleSiteDirStyle(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length) {
3839  
3840      // Ignore the results.  If it took it fine and if not, it'll
3841      // just have to do.
3842      if ((ctxt->_result >= 200) && (ctxt->_result < 300)) {
3843      
3844          CFStringRef system = CFStringCreateWithBytes(CFGetAllocator(ftpCtxt->_properties),
3845                                                       line,
3846                                                       length,
3847                                                       kCFStringEncodingUTF8,
3848                                                       FALSE);
3849          CFRange range = CFRangeMake(0, CFStringGetLength(system));
3850  
3851          if (CFStringFindWithOptions(system, kCFFTPMSDOSSystemString, range, 0, NULL)) {
3852              ctxt->_state = kFTPStateSITEDIRSTYLE;
3853              _WriteCommand(ctxt, ftpCtxt, kCFFTPSITEDIRSTYLECommandString);
3854              CFRelease(system);
3855              return;
3856          }
3857          
3858          CFRelease(system);
3859      }
3860  
3861      if( __CFBitIsSet(ctxt->_flags, kFlagBitIsXServer)) {
3862          ctxt->_state = kFTPStateSITETRUTH;
3863          _WriteCommand(ctxt, ftpCtxt, kCFFTPSITETRUTHCommandString);
3864      } else { 
3865          ctxt->_state = kFTPStatePWD;
3866          _WriteCommand(ctxt, ftpCtxt, kCFFTPPWDCommandString); 
3867      }
3868  }
3869  
3870  
3871  /* static */ void
3872  _HandleSiteTruth(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
3873  
3874      // Ignore the results.  If it took it fine and if not, it'll
3875      // just have to do.
3876  
3877      ctxt->_state = kFTPStatePWD;
3878      _WriteCommand(ctxt, ftpCtxt, kCFFTPPWDCommandString);
3879  }
3880  
3881  
3882  /* static */ void
3883  _HandlePrintWorkingDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length) {
3884  
3885      // Valid returns for PWD are:
3886      //		257, 421, 500, 501, 502, 550
3887  
3888      // Only care about trying to parse out the root directory
3889      // if PWD succeeded.
3890      if ((ctxt->_result >= 200) && (ctxt->_result < 300)) {
3891          
3892          const UInt8* first = memchr(line, '"', length);
3893          
3894          if (first) {
3895              const UInt8* last = line + length - 1;
3896              
3897              first++;
3898              
3899              while  ( last != first) {
3900                  if (*last == '"') {
3901                      
3902                      if (*(last - 1) == '/')
3903                          last--;
3904                      
3905                      ctxt->_root = CFStringCreateWithBytes(CFGetAllocator(ftpCtxt->_properties),
3906                                                            first,
3907                                                            last - first,
3908                                                            kCFStringEncodingUTF8,
3909                                                            FALSE);
3910                      break;
3911                  }
3912                  
3913                  last--;
3914              }
3915          }
3916      }
3917  
3918      ctxt->_state = kFTPStateTYPE;
3919      _WriteCommand(ctxt, ftpCtxt, kCFFTPTYPECommandString);
3920  }
3921  
3922  
3923  /* static */ void
3924  _HandleType(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
3925  
3926      // Valid returns for TYPE are:
3927      //		200, 421, 501, 504, 530
3928  
3929      if ((ctxt->_result < 200) || (ctxt->_result >= 300)) {
3930          CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
3931          _ReportError(ftpCtxt, &error);
3932      }
3933  
3934      else {
3935          ctxt->_state = kFTPStateIdle;
3936          _StartProcess(ctxt, ftpCtxt);
3937      }
3938  }
3939  
3940  
3941  /* static */ void
3942  _HandleChangeDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
3943  
3944      // Valid returns for CWD are:
3945      //		250, 421, 500, 501, 502, 530, 550
3946  
3947      if (__CFBitIsSet(ctxt->_flags, kFlagBitLeftForDead) && (ctxt->_result == 421)) {
3948          
3949          CFStreamError error;
3950          Boolean openComplete;
3951          
3952          _CFNetConnectionLost(ftpCtxt->_connection);
3953          _CFNetConnectionDequeue(ftpCtxt->_connection, ftpCtxt);
3954          
3955          _FTPStreamOpen(ftpCtxt->_userStream, &error, &openComplete, ftpCtxt);
3956      }
3957  
3958      else if ((ctxt->_result >= 300) || (ctxt->_result < 200)){
3959          CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
3960  	__CFBitClear(ctxt->_flags, kFlagBitLeftForDead);
3961          _ReportError(ftpCtxt, &error);
3962      }
3963  
3964      else {
3965  
3966          CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
3967          __CFBitClear(ctxt->_flags, kFlagBitLeftForDead);
3968          
3969          // Only allow the special SPI stuff on the WriteStream for now
3970          if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformUpload)) {
3971  
3972              CFStringRef target = CFURLCopyLastPathComponent(ftpCtxt->_url);
3973  			
3974              // Performing a RMD or DELE
3975              if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitRemoveResource)) {
3976  
3977                  CFStringRef cmd;
3978  
3979                  if (CFURLHasDirectoryPath(ftpCtxt->_url) || _IsRoot(ftpCtxt->_url)) {
3980                      ctxt->_state = kFTPStateRMD;
3981                      cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRMDCommandString, target);
3982                  }
3983                  else {
3984                      ctxt->_state = kFTPStateDELE;
3985                      cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPDELECommandString, target);
3986                  }
3987  
3988                  _WriteCommand(ctxt, ftpCtxt, cmd);
3989                  CFRelease(cmd);
3990  
3991                  CFWriteStreamSignalEvent((CFWriteStreamRef)ftpCtxt->_userStream, kCFStreamEventOpenCompleted, NULL);
3992              }
3993  
3994              // Performing RNFR->RNTO
3995              else if (ftpCtxt->_newUrl) {
3996                  CFStringRef cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRNFRCommandString, target);
3997                  ctxt->_state = kFTPStateRNFR;
3998                  _WriteCommand(ctxt, ftpCtxt, cmd);
3999                  CFRelease(cmd);
4000  
4001                  CFWriteStreamSignalEvent((CFWriteStreamRef)ftpCtxt->_userStream, kCFStreamEventOpenCompleted, NULL);
4002              }
4003  
4004              // Performing MKD
4005              else if (CFURLHasDirectoryPath(ftpCtxt->_url) || _IsRoot(ftpCtxt->_url)) {
4006                  CFStringRef cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPMKDCommandString, target);
4007                  ctxt->_state = kFTPStateMKD;
4008                  _WriteCommand(ctxt, ftpCtxt, cmd);
4009                  CFRelease(cmd);
4010  
4011                  CFWriteStreamSignalEvent((CFWriteStreamRef)ftpCtxt->_userStream, kCFStreamEventOpenCompleted, NULL);
4012              }
4013  
4014              if (target) CFRelease(target);
4015          }
4016  
4017          if (ctxt->_state == kFTPStateCWD) {
4018  
4019              if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformPASV)) {
4020                  UInt8 buf[SOCK_MAXADDRLEN];
4021                  u_char family = _GetProtocolFamily(ftpCtxt, buf);
4022  
4023                  ctxt->_state = kFTPStatePASV;
4024                  if (family == AF_INET)
4025                      _WriteCommand(ctxt, ftpCtxt, kCFFTPPASVCommandString);
4026                  else if (family == AF_INET6)
4027                      _WriteCommand(ctxt, ftpCtxt, kCFFTPEPSVCommandString);
4028                  else {
4029                      CFStreamError error = {kCFStreamErrorDomainFTP, 522}; //unkown protocol
4030                      _ReportError(ftpCtxt, &error);
4031                  }
4032              }
4033  
4034              else if (_CreateListenerForContext(alloc, ftpCtxt)) {
4035                  CFDataRef addr = CFSocketCopyAddress(ftpCtxt->_server);
4036                  CFStringRef cmd = NULL;
4037                  struct sockaddr_in* sa = (struct sockaddr_in*)CFDataGetBytePtr(addr);
4038                  struct sockaddr_in6* sa6 = (struct sockaddr_in6*)sa;
4039  
4040                  if (sa->sin_family == AF_INET) { //ipv4
4041                      cmd = CFStringCreateWithFormat(alloc,
4042                                                     NULL,
4043                                                     kCFFTPPORTCommandString,
4044                                                     (unsigned int)((UInt8*)(&(sa->sin_addr)))[0],
4045                                                     (unsigned int)((UInt8*)(&(sa->sin_addr)))[1],
4046                                                     (unsigned int)((UInt8*)(&(sa->sin_addr)))[2],
4047                                                     (unsigned int)((UInt8*)(&(sa->sin_addr)))[3],
4048                                                     (unsigned int)((UInt8*)(&(sa->sin_port)))[0],
4049                                                     (unsigned int)((UInt8*)(&(sa->sin_port)))[1]);
4050                  } else if (sa->sin_family == AF_INET6) { //ipv6
4051                      cmd = CFStringCreateWithFormat(alloc,
4052                                                     NULL,
4053                                                     kCFFTPEPRTCommandString,
4054                                                     (unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[0]),
4055                                                     (unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[1]),
4056                                                     (unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[2]),
4057                                                     (unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[3]),
4058                                                     (unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[4]),
4059                                                     (unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[5]),
4060                                                     (unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[6]),
4061                                                     (unsigned int)ntohs(((UInt16*)(&(sa6->sin6_addr)))[7]),
4062                                                     (unsigned int)ntohs(((UInt16*)(&(sa6->sin6_port)))[0]));
4063                  } else { // bail out for unknown protocol
4064                      CFStreamError error = {kCFStreamErrorDomainFTP, 522}; //unkown protocol
4065                      _ReportError(ftpCtxt, &error);
4066                  }
4067  
4068                  if (cmd) {
4069                      ctxt->_state = kFTPStatePORT;
4070                      _WriteCommand(ctxt, ftpCtxt, cmd);
4071                      CFRelease(cmd);
4072                  }
4073              }
4074              else {
4075                  CFStreamError error = {kCFStreamErrorDomainPOSIX, errno};
4076  #if defined(__WIN32__)
4077  		if (!error.error) {
4078  		    error.error = WSAGetLastError();
4079  		    if (error.error)
4080  			error.domain = kCFStreamErrorDomainWinSock;
4081  		}
4082  #endif
4083                  if (!error.error) {
4084                      error.error = ENOTCONN;
4085  		    error.domain = _kCFStreamErrorDomainNativeSockets;
4086  		}
4087                  _ReportError(ftpCtxt, &error);
4088              }
4089          }
4090      }
4091  }
4092  
4093  
4094  /* static */ void
4095  _HandlePassive(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length) {
4096  
4097      // Valid returns for PASV are:
4098      //		227, 421, 500, 501, 502, 530
4099  
4100      if ((ctxt->_result < 200) || (ctxt->_result >= 300)) {
4101          CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
4102          _ReportError(ftpCtxt, &error);
4103      }
4104      else {
4105          UInt8 buf[SOCK_MAXADDRLEN];
4106          u_char family = _GetProtocolFamily(ftpCtxt, buf);
4107          struct sockaddr_in addr4;
4108          struct sockaddr_in6 addr6;
4109                  
4110          if (family == AF_INET6)
4111              memcpy(&addr6, buf, sizeof(struct sockaddr_in6));
4112              
4113          if ((family == AF_INET && !_PASVAddressParser(line, &addr4)) ||
4114              (family == AF_INET6 && !_EPSVPortParser(line, &addr6)))
4115          {
4116              CFStreamError error = {_kCFStreamErrorDomainNativeSockets, EADDRNOTAVAIL};
4117              _ReportError(ftpCtxt, &error);
4118          }
4119          else {
4120              CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
4121              CFStreamClientContext streamCtxt = {0, ftpCtxt, NULL, NULL, NULL};
4122              CFSocketSignature sig;
4123      
4124              sig.protocolFamily = family;
4125              sig.socketType = SOCK_STREAM;
4126              sig.protocol = IPPROTO_TCP;
4127              if (family == AF_INET)
4128                  sig.address = CFDataCreate(alloc, (const UInt8*)&addr4, sizeof(addr4));
4129              else
4130                  sig.address = CFDataCreate(alloc, (const UInt8*)&addr6, sizeof(addr6));
4131  
4132              if (!sig.address) {
4133                  CFStreamError error = {kCFStreamErrorDomainPOSIX, ENOMEM};
4134                  _ReportError(ftpCtxt, &error);
4135                  return;
4136              }
4137              
4138              if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformUpload))
4139  				_CFSocketStreamCreatePair(alloc, NULL, 0, 0, &sig, NULL, (CFWriteStreamRef*)&ftpCtxt->_dataStream);
4140              else
4141  				_CFSocketStreamCreatePair(alloc, NULL, 0, 0, &sig, (CFReadStreamRef*)&ftpCtxt->_dataStream, NULL);
4142  
4143              CFRelease(sig.address);
4144  
4145              if (!ftpCtxt->_dataStream) {
4146                  CFStreamError error = {kCFStreamErrorDomainPOSIX, ENOMEM};
4147                  _ReportError(ftpCtxt, &error);
4148                  return;
4149              }
4150  
4151              if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformUpload))
4152                  CFWriteStreamSetClient((CFWriteStreamRef)ftpCtxt->_dataStream, ~0L, (CFWriteStreamClientCallBack)_DataStreamCallBack, &streamCtxt); 
4153              else 
4154                  CFReadStreamSetClient((CFReadStreamRef)ftpCtxt->_dataStream, ~0L, (CFReadStreamClientCallBack)_DataStreamCallBack, &streamCtxt); 
4155  			
4156  			_CFTypeScheduleOnMultipleRunLoops(ftpCtxt->_dataStream, ftpCtxt->_runloops);
4157              
4158              CFDictionaryApplyFunction(ftpCtxt->_properties, (CFDictionaryApplierFunction)_StreamPropertyApplier, (void*)ftpCtxt->_dataStream);
4159  
4160              _StartTransfer(ctxt, ftpCtxt);
4161          }
4162      }
4163  }
4164  
4165  
4166  /* static */ void
4167  _HandlePort(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
4168  
4169      // Valid returns for PORT are:
4170      //		200, 421, 500, 501, 530
4171      
4172      if ((ctxt->_result < 200) || (ctxt->_result >= 300)) {
4173          CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
4174          _ReportError(ftpCtxt, &error);
4175      }
4176      else
4177          _StartTransfer(ctxt, ftpCtxt);
4178  }
4179  
4180  
4181  /* static */ void
4182  _HandleStat(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length, Boolean isMultiLine) {
4183  
4184      // Valid returns for STAT are:
4185      //		211, 212, 213, 421, 450, 500, 501, 502, 530
4186  
4187      if (!ftpCtxt->_attributes) {
4188  
4189          CFFTPCreateParsedResourceListing(CFGetAllocator(ftpCtxt->_properties),
4190                                           line,
4191                                           length,
4192                                           &ftpCtxt->_attributes);
4193      }
4194  
4195      if (isMultiLine)
4196          return;
4197  
4198      if (isdigit(line[0])) {
4199  
4200          CFStringRef cmd;
4201          CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
4202      
4203  		if (!ftpCtxt->_offset) {
4204  			CFStringRef path = _CreatePathForContext(alloc, ctxt, ftpCtxt);
4205  
4206  			cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRETRCommandString, path);
4207  			ctxt->_state = kFTPStateRETR;
4208  			__CFBitClear(ftpCtxt->_flags, kFlagBitCompleteDeferred);
4209  			CFRelease(path);
4210  
4211  			if (ftpCtxt->_dataStream)
4212  				CFReadStreamOpen((CFReadStreamRef)ftpCtxt->_dataStream);
4213  		}
4214  		else {
4215  			cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRESTCommandString, ftpCtxt->_offset);
4216  			ctxt->_state = kFTPStateREST;
4217  		}
4218  
4219          _WriteCommand(ctxt, ftpCtxt, cmd);
4220          CFRelease(cmd);
4221      }
4222  }
4223  
4224  
4225  /* static */ void
4226  _HandleSize(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt, const UInt8* line, CFIndex length) {
4227  
4228      // Valid returns for SIZE are (pulled from IETF working document, may not be set in stone):
4229      //		213, 500, 501, 502, 550
4230  
4231      CFStringRef cmd;
4232      CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
4233  	
4234  	// If SIZE failed, attempt to issue the STAT command instead.
4235  	if ((ctxt->_result >= 500) && (ctxt->_result < 600)) {
4236  		
4237  		CFStringRef target = CFURLCopyLastPathComponent(ftpCtxt->_url);
4238  		
4239  		if (ftpCtxt->_attributes) {
4240  			CFRelease(ftpCtxt->_attributes);
4241  			ftpCtxt->_attributes = NULL;
4242  		}
4243  		
4244  		ctxt->_state = kFTPStateSTAT;
4245  		cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPSTATCommandString, target);
4246  		if (target) CFRelease(target);
4247  	}
4248      
4249      else {
4250  		
4251  		if (ctxt->_result == 213) {
4252  
4253  			CFIndex i = 0;
4254  			
4255  			while ((i < length) && isdigit(line[i]))
4256  				i++;
4257  
4258  			while ((i < length) && !isdigit(line[i]))
4259  				i++;
4260  
4261  			if (i < length) {
4262  				UInt8* end = NULL;
4263  	#if defined(__WIN32__)
4264  				long long s = _atoi64(&line[i]);
4265  	#else
4266  				long long s = strtoull((const char*)&line[i], (char**)&end, 0);
4267  	#endif
4268  				if (!(((s == ULLONG_MAX) && (errno)) || ((s == 0) && (end == &line[i])))) {
4269  					const void *keys[1], *values[1];
4270  
4271  					keys[0]   = kCFFTPResourceSize;
4272  					values[0] = CFNumberCreate(alloc, kCFNumberLongLongType, &s);
4273  
4274  					if (values[0]) {
4275  						ftpCtxt->_attributes = CFDictionaryCreate(alloc, keys, values, 1,
4276  																  &kCFTypeDictionaryKeyCallBacks,
4277  																  &kCFTypeDictionaryValueCallBacks);
4278  		
4279  						CFRelease(values[0]);
4280  					}
4281  				}
4282  			}
4283  			
4284  		}
4285  
4286  		if (!ftpCtxt->_offset) {
4287  			CFStringRef path = _CreatePathForContext(alloc, ctxt, ftpCtxt);
4288  
4289  			cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRETRCommandString, path);
4290  			ctxt->_state = kFTPStateRETR;
4291  			__CFBitClear(ftpCtxt->_flags, kFlagBitCompleteDeferred);
4292  			CFRelease(path);
4293  
4294  			if (ftpCtxt->_dataStream)
4295  				CFReadStreamOpen((CFReadStreamRef)ftpCtxt->_dataStream);
4296  		}
4297  		else {
4298  			cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRESTCommandString, ftpCtxt->_offset);
4299  			ctxt->_state = kFTPStateREST;
4300  		}
4301  	}
4302  
4303  	_WriteCommand(ctxt, ftpCtxt, cmd);
4304  	CFRelease(cmd);
4305  }
4306  
4307  
4308  /* static */ void
4309  _HandleRestart(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
4310  
4311      // Valid returns for REST are:
4312      //		350, 421, 500, 501, 502, 530
4313  
4314      if ((ctxt->_result < 300) || (ctxt->_result >= 400)) {
4315          CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
4316          _ReportError(ftpCtxt, &error);
4317      }
4318      
4319      else {
4320          CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
4321          CFStringRef cmd, path = _CreatePathForContext(alloc, ctxt, ftpCtxt);
4322      
4323          cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRETRCommandString, path);
4324          CFRelease(path);
4325          
4326          ctxt->_state = kFTPStateRETR;
4327                 __CFBitClear(ftpCtxt->_flags, kFlagBitCompleteDeferred);
4328                  
4329          if (ftpCtxt->_dataStream)
4330              CFReadStreamOpen((CFReadStreamRef)ftpCtxt->_dataStream);
4331          
4332          _WriteCommand(ctxt, ftpCtxt, cmd);
4333          CFRelease(cmd);
4334      }
4335  }
4336  
4337  
4338  /* static */ void
4339  _HandleRetrieve(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
4340  
4341      // Valid returns for RETR are:
4342      //		110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, 550
4343  
4344      if (ctxt->_result < 200)
4345          return;
4346  
4347      if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
4348          
4349          ctxt->_state = kFTPStateIdle;
4350          _StartProcess(ctxt, ftpCtxt);
4351      }
4352      else {
4353      
4354          if (ctxt->_result >= 300) {
4355              CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
4356              _ReportError(ftpCtxt, &error);
4357          }
4358          
4359          else if (ctxt->_result >= 200) {
4360              _ConnectionComplete(ctxt, ftpCtxt);
4361          }
4362      }
4363  }
4364  
4365  
4366  /* static */ void
4367  _HandleNameList(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
4368  
4369      // Valid returns for NLST are:
4370      //		125, 150, 226, 250, 425, 426, 451, 450, 500, 501, 502, 421, 530
4371      
4372      if (ctxt->_result < 200)
4373          return;
4374          
4375      if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
4376          
4377          ctxt->_state = kFTPStateIdle;
4378          _StartProcess(ctxt, ftpCtxt);
4379      }
4380      else {
4381          if (ctxt->_result >= 300) {
4382              CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
4383              _ReportError(ftpCtxt, &error);
4384          }
4385          
4386          else {
4387              _ConnectionComplete(ctxt, ftpCtxt);
4388          }
4389      }
4390  }
4391  
4392  
4393  /* static */ void
4394  _HandleList(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
4395  	
4396      // Valid returns for LIST are:
4397      //		125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, 530
4398  	
4399      if (ctxt->_result < 200)
4400          return;
4401      
4402      if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
4403          
4404          ctxt->_state = kFTPStateIdle;
4405          _StartProcess(ctxt, ftpCtxt);
4406      }
4407      else {
4408          if (ctxt->_result >= 300) {
4409              CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
4410              _ReportError(ftpCtxt, &error);
4411          }
4412          else if (ctxt->_result < 300) {
4413              _ConnectionComplete(ctxt, ftpCtxt);
4414          }
4415      }
4416  }
4417  
4418  
4419  /* static */ void
4420  _HandleStore(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
4421  
4422      // Valid returns for STOR are:
4423      //		110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 452,
4424      //		500, 501, 530, 532, 551, 552, 553
4425  
4426      if (ctxt->_result < 200)
4427          return;
4428      
4429      if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
4430          
4431          ctxt->_state = kFTPStateIdle;
4432          _StartProcess(ctxt, ftpCtxt);
4433      }
4434      else {
4435          if (ctxt->_result >= 300) {
4436              CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
4437              _ReportError(ftpCtxt, &error);
4438          }
4439          else if (ctxt->_result < 300) {
4440              _ConnectionComplete(ctxt, ftpCtxt);
4441          }
4442      }
4443  }
4444  
4445  
4446  /* static */ void
4447  _HandleMakeDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
4448  
4449      // Valid returns for MKD are:
4450      //		257, 421, 500, 501, 502, 530, 550
4451  
4452      if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
4453              
4454          ctxt->_state = kFTPStateIdle;
4455          _StartProcess(ctxt, ftpCtxt);
4456      }
4457      else {
4458          if ((ctxt->_result >= 300) || (ctxt->_result < 200)) {
4459              CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
4460              _ReportError(ftpCtxt, &error);
4461          }
4462          else {
4463              _ConnectionComplete(ctxt, ftpCtxt);
4464          }
4465      }
4466  }
4467  
4468  
4469  /* static */ void
4470  _HandleRemoveDirectory(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
4471  
4472      // Valid returns for RMD are:
4473      //		250, 421, 500, 501, 502, 530, 550
4474  
4475      if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
4476              
4477          ctxt->_state = kFTPStateIdle;
4478          _StartProcess(ctxt, ftpCtxt);
4479      }
4480      else {
4481          if ((ctxt->_result >= 300) || (ctxt->_result < 200)) {
4482              CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
4483              _ReportError(ftpCtxt, &error);
4484          }
4485          else {
4486              _ConnectionComplete(ctxt, ftpCtxt);
4487          }
4488      }
4489  }
4490  
4491  
4492  /* static */ void
4493  _HandleDelete(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
4494  
4495      // Valid returns for DELE are:
4496      //		250, 421, 450, 500, 501, 502, 530, 550
4497  
4498      if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
4499              
4500          ctxt->_state = kFTPStateIdle;
4501          _StartProcess(ctxt, ftpCtxt);
4502      }
4503      else {
4504          if ((ctxt->_result >= 300) || (ctxt->_result < 200)) {
4505              CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
4506              _ReportError(ftpCtxt, &error);
4507          }
4508          else {
4509              _ConnectionComplete(ctxt, ftpCtxt);
4510          }
4511      }
4512  }
4513  
4514  
4515  /* static */ void
4516  _HandleRenameFrom(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
4517  
4518  	// Valid returns for RNFR are:
4519  	//		350, 421, 450, 500, 501, 502, 530, 550
4520      
4521      if ((ctxt->_result < 300) || (ctxt->_result >= 400)) {
4522          CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
4523          _ReportError(ftpCtxt, &error);
4524      }
4525      else {
4526          CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
4527          CFStringRef cmd, path;
4528          CFURLRef url = ftpCtxt->_url;
4529          
4530          // **FIXME** Total hack for easily getting the path for the new URL.
4531          ftpCtxt->_url = ftpCtxt->_newUrl;
4532          path = _CreatePathForContext(alloc, ctxt, ftpCtxt);
4533          ftpCtxt->_url = url;
4534          
4535          ctxt->_state = kFTPStateRNTO;
4536          cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPRNTOCommandString, path);
4537          CFRelease(path);
4538          
4539          _WriteCommand(ctxt, ftpCtxt, cmd);
4540          CFRelease(cmd);
4541      }
4542  }
4543  
4544  
4545  /* static */ void
4546  _HandleRenameTo(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
4547  
4548      // Valid returns for RNTO are:
4549      //		250, 421, 500, 501, 502, 503, 530, 532, 553
4550  
4551      if (__CFBitIsSet(ctxt->_flags, kFlagBitReturnToIdle)) {
4552              
4553          ctxt->_state = kFTPStateIdle;
4554          _StartProcess(ctxt, ftpCtxt);
4555      }
4556      else {
4557          if ((ctxt->_result >= 300) || (ctxt->_result < 200)) {
4558              CFStreamError error = {kCFStreamErrorDomainFTP, ctxt->_result};
4559              _ReportError(ftpCtxt, &error);
4560          }
4561          else {
4562              _ConnectionComplete(ctxt, ftpCtxt);
4563          }
4564      }
4565  }
4566  
4567  
4568  /* static */ void
4569  _StartProcess(_CFFTPNetConnectionContext* ctxt, _CFFTPStreamContext* ftpCtxt) {
4570      
4571      if (__CFBitIsSet(ftpCtxt->_flags, kFlagBitLogInOnly)) {
4572          if (CFGetTypeID(ftpCtxt->_userStream) == CFReadStreamGetTypeID())
4573              CFReadStreamSignalEvent((CFReadStreamRef)ftpCtxt->_userStream, kCFStreamEventOpenCompleted, NULL);
4574          else
4575              CFWriteStreamSignalEvent((CFWriteStreamRef)ftpCtxt->_userStream, kCFStreamEventOpenCompleted, NULL);
4576          _ConnectionComplete(ctxt, ftpCtxt);
4577      }
4578      else {
4579  
4580          CFStringRef path, cmd;
4581          CFAllocatorRef alloc = CFGetAllocator(ftpCtxt->_properties);
4582          
4583          __CFBitClear(ctxt->_flags, kFlagBitReturnToIdle);
4584  
4585          // Check if this request is for a directory listing.  If so, need to CWD
4586          // all the way to the give url.  All other requests CWD to the directory
4587          // above the last, path component.
4588          if (!__CFBitIsSet(ftpCtxt->_flags, kFlagBitPerformUpload) &&
4589              (CFURLHasDirectoryPath(ftpCtxt->_url) || _IsRoot(ftpCtxt->_url)))
4590          {
4591              path = _CreatePathForContext(alloc, ctxt, ftpCtxt);
4592          }
4593          else {
4594              // **FIXME** Total hack for easily getting the path without the last component.
4595              CFURLRef old = ftpCtxt->_url;
4596              ftpCtxt->_url = CFURLCreateCopyDeletingLastPathComponent(alloc, old);
4597              path = _CreatePathForContext(alloc, ctxt, ftpCtxt);
4598              CFRelease(ftpCtxt->_url);
4599              ftpCtxt->_url = old;
4600          }
4601          
4602          cmd = CFStringCreateWithFormat(alloc, NULL, kCFFTPCWDCommandString, path);
4603          CFRelease(path);
4604          
4605          ctxt->_state = kFTPStateCWD;
4606          _WriteCommand(ctxt, ftpCtxt, cmd);
4607          
4608          CFRelease(cmd);
4609      }
4610  }
4611  
4612  
4613  #if 0
4614  #pragma mark -
4615  #pragma mark Extern Function Definitions (API)
4616  #endif
4617  
4618  
4619  
4620  /* CF_EXPORT */ CFWriteStreamRef
4621  CFWriteStreamCreateWithFTPURL(CFAllocatorRef alloc, CFURLRef ftpURL) {
4622      
4623      CFWriteStreamRef result = NULL;
4624      CFStringRef temp;
4625      _CFFTPStreamContext* ctxt;
4626      CFStringRef username = NULL;
4627      CFStringRef password = NULL;
4628  
4629  
4630      if (!ftpURL || !(ftpURL = _ConvertToCFFTPHappyURL(ftpURL)))
4631          return result;
4632      
4633      temp = CFURLCopyScheme(ftpURL);
4634      if (!temp) {
4635          CFRelease(ftpURL);
4636          return result;
4637      }
4638          
4639      if ((CFStringCompare(temp, kFTPSchemeString, 0) != kCFCompareEqualTo) &&
4640          (CFStringCompare(temp, kFTPSSchemeString, 0) != kCFCompareEqualTo))
4641      {
4642          CFRelease(ftpURL);
4643          CFRelease(temp);
4644          return result;
4645      }
4646      
4647      CFRelease(temp);
4648      
4649      temp = CFURLCopyHostName(ftpURL);
4650      if (!temp) {
4651          CFRelease(ftpURL);
4652          return result;
4653      }
4654      
4655      CFRelease(temp);
4656  
4657      // get and validate username/password (if any)
4658      username = CFURLCopyUserName(ftpURL);
4659      if (username) {
4660  	 if (!_ValidFTPString(username)) {
4661  	    CFRelease(username);
4662  	    return result;
4663  	 }
4664      }
4665      password = CFURLCopyPassword(ftpURL);
4666      if (password) {
4667  	 if (!_ValidFTPString(password)) {
4668  	    if (username) {
4669  		CFRelease(username);
4670  	    }
4671  	    CFRelease(password);
4672  	    return result;
4673  	 }
4674      }
4675      
4676      ctxt = (_CFFTPStreamContext*)CFAllocatorAllocate(alloc,
4677                                                       sizeof(ctxt[0]),
4678                                                       0);
4679      if (ctxt) {
4680  
4681          memset(ctxt, 0, sizeof(ctxt[0]));
4682          
4683          __CFBitSet(ctxt->_flags, kFlagBitPerformPASV);
4684          __CFBitSet(ctxt->_flags, kFlagBitPerformUpload);
4685  
4686          ctxt->_url = CFURLCopyAbsoluteURL(ftpURL);
4687  
4688          ctxt->_runloops = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
4689          ctxt->_properties = CFDictionaryCreateMutable(alloc,
4690                                                        0,
4691                                                        &kCFTypeDictionaryKeyCallBacks,
4692                                                        &kCFTypeDictionaryValueCallBacks);
4693  
4694          if (ctxt->_url && ctxt->_runloops && ctxt->_properties) {
4695  			
4696  			CFWriteStreamCallBacksV1 _FTPWriteStreamCallBacks;
4697  
4698  			memset(&_FTPWriteStreamCallBacks, 0, sizeof(_FTPWriteStreamCallBacks));
4699  			
4700  			_FTPWriteStreamCallBacks.version = 1;
4701  			_FTPWriteStreamCallBacks.finalize = (void (*)(CFWriteStreamRef, void*))_FTPStreamFinalize;
4702  			_FTPWriteStreamCallBacks.copyDescription = (CFStringRef (*)(CFWriteStreamRef, void*))_FTPStreamCopyDescription;
4703  			_FTPWriteStreamCallBacks.open = (Boolean (*)(CFWriteStreamRef, CFStreamError*, Boolean*, void*))_FTPStreamOpen;
4704  			_FTPWriteStreamCallBacks.openCompleted = (Boolean (*)(CFWriteStreamRef, CFStreamError*, void*))_FTPStreamOpenCompleted;
4705  			_FTPWriteStreamCallBacks.write = (CFIndex (*)(CFWriteStreamRef, const UInt8*, CFIndex, CFStreamError*, void*))_FTPStreamWrite;
4706  			_FTPWriteStreamCallBacks.canWrite = (Boolean (*)(CFWriteStreamRef, void*))_FTPStreamCanWrite;
4707  			_FTPWriteStreamCallBacks.close = (void (*)(CFWriteStreamRef, void*))_FTPStreamClose;
4708  			_FTPWriteStreamCallBacks.copyProperty = (CFTypeRef (*)(CFWriteStreamRef, CFStringRef, void*))_FTPStreamCopyProperty;
4709  			_FTPWriteStreamCallBacks.setProperty = (Boolean (*)(CFWriteStreamRef, CFStringRef, CFTypeRef, void*))_FTPStreamSetProperty;
4710  			_FTPWriteStreamCallBacks.schedule = (void (*)(CFWriteStreamRef, CFRunLoopRef, CFStringRef, void*))_FTPStreamSchedule;
4711  			_FTPWriteStreamCallBacks.unschedule = (void (*)(CFWriteStreamRef, CFRunLoopRef, CFStringRef, void*))_FTPStreamUnschedule;
4712  			
4713              result = CFWriteStreamCreate(alloc, (CFWriteStreamCallBacks*)&_FTPWriteStreamCallBacks, ctxt);
4714  		}
4715      
4716          if (result) {
4717  
4718              ctxt->_userStream = result;		// Don't retain for fear of loop.
4719              
4720              if (username) {
4721  		// the username in the ftpURL was validated above
4722  		CFWriteStreamSetProperty(result, kCFStreamPropertyFTPUserName_prevalidated, username);
4723              }
4724  	    if (password) {
4725  		// the password in the ftpURL was validated above
4726  		CFWriteStreamSetProperty(result, kCFStreamPropertyFTPPassword_prevalidated, password);
4727  	    }
4728          }
4729          else {
4730  
4731              if (ctxt->_url)
4732                  CFRelease(ctxt->_url);
4733  
4734              if (ctxt->_runloops)
4735                  CFRelease(ctxt->_runloops);
4736  
4737              if (ctxt->_properties)
4738                  CFRelease(ctxt->_properties);
4739  
4740              CFAllocatorDeallocate(alloc, ctxt);
4741          }
4742      }
4743      
4744      CFRelease(ftpURL);
4745      
4746      if (username) {
4747  	CFRelease(username);
4748      }
4749      if (password) {
4750  	CFRelease(password);
4751      }
4752      
4753      return result;
4754  }
4755  
4756  /* CF_EXPORT */ CFReadStreamRef
4757  CFReadStreamCreateWithFTPURL(CFAllocatorRef alloc, CFURLRef ftpURL) {
4758  
4759      CFReadStreamRef result = NULL;
4760      CFStringRef temp;
4761      _CFFTPStreamContext* ctxt;
4762      CFStringRef username = NULL;
4763      CFStringRef password = NULL;
4764  
4765  
4766      if (!ftpURL || !(ftpURL = _ConvertToCFFTPHappyURL(ftpURL)))
4767          return result;
4768      
4769      temp = CFURLCopyScheme(ftpURL);
4770      if (!temp) {
4771          CFRelease(ftpURL);
4772          return result;
4773      }
4774      
4775      if ((CFStringCompare(temp, kFTPSchemeString, 0) != kCFCompareEqualTo) &&
4776          (CFStringCompare(temp, kFTPSSchemeString, 0) != kCFCompareEqualTo))
4777      {
4778          CFRelease(ftpURL);
4779          CFRelease(temp);
4780          return result;
4781      }
4782      
4783      CFRelease(temp);
4784      
4785      temp = CFURLCopyHostName(ftpURL);
4786      if (!temp) {
4787          CFRelease(ftpURL);
4788          return result;
4789      }
4790      
4791      CFRelease(temp);
4792      
4793      // get and validate username/password (if any)
4794      username = CFURLCopyUserName(ftpURL);
4795      if (username) {
4796  	 if (!_ValidFTPString(username)) {
4797  	    CFRelease(username);
4798  	    return result;
4799  	 }
4800      }
4801      password = CFURLCopyPassword(ftpURL);
4802      if (password) {
4803  	 if (!_ValidFTPString(password)) {
4804  	    if (username) {
4805  		CFRelease(username);
4806  	    }
4807  	    CFRelease(password);
4808  	    return result;
4809  	 }
4810      }
4811      
4812      ctxt = (_CFFTPStreamContext*)CFAllocatorAllocate(alloc,
4813                                                       sizeof(ctxt[0]),
4814                                                       0);
4815      if (ctxt) {
4816  
4817          memset(ctxt, 0, sizeof(ctxt[0]));
4818  
4819          __CFBitSet(ctxt->_flags, kFlagBitPerformPASV);
4820          
4821          ctxt->_url = CFURLCopyAbsoluteURL(ftpURL);
4822  
4823          ctxt->_runloops = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
4824          ctxt->_properties = CFDictionaryCreateMutable(alloc,
4825                                                        0,
4826                                                        &kCFTypeDictionaryKeyCallBacks,
4827                                                        &kCFTypeDictionaryValueCallBacks);
4828  
4829          if (ctxt->_url && ctxt->_runloops && ctxt->_properties) {
4830  			
4831  			CFReadStreamCallBacksV1 _FTPReadStreamCallBacks;
4832  
4833  			memset(&_FTPReadStreamCallBacks, 0, sizeof(_FTPReadStreamCallBacks));
4834  			
4835  			_FTPReadStreamCallBacks.version = 1;
4836  			_FTPReadStreamCallBacks.finalize = (void (*)(CFReadStreamRef, void*))_FTPStreamFinalize;
4837  			_FTPReadStreamCallBacks.copyDescription = (CFStringRef (*)(CFReadStreamRef, void*))_FTPStreamCopyDescription;
4838  			_FTPReadStreamCallBacks.open = (Boolean (*)(CFReadStreamRef, CFStreamError*, Boolean*, void*))_FTPStreamOpen;
4839  			_FTPReadStreamCallBacks.openCompleted = (Boolean (*)(CFReadStreamRef, CFStreamError*, void*))_FTPStreamOpenCompleted;
4840  			_FTPReadStreamCallBacks.read = (CFIndex (*)(CFReadStreamRef, UInt8*, CFIndex, CFStreamError*, Boolean*, void*))_FTPStreamRead;
4841  			_FTPReadStreamCallBacks.canRead = (Boolean (*)(CFReadStreamRef, void*))_FTPStreamCanRead;
4842  			_FTPReadStreamCallBacks.close = (void (*)(CFReadStreamRef, void*))_FTPStreamClose;
4843  			_FTPReadStreamCallBacks.copyProperty = (CFTypeRef (*)(CFReadStreamRef, CFStringRef, void*))_FTPStreamCopyProperty;
4844  			_FTPReadStreamCallBacks.setProperty = (Boolean (*)(CFReadStreamRef, CFStringRef, CFTypeRef, void*))_FTPStreamSetProperty;
4845  			_FTPReadStreamCallBacks.schedule = (void (*)(CFReadStreamRef, CFRunLoopRef, CFStringRef, void*))_FTPStreamSchedule;
4846  			_FTPReadStreamCallBacks.unschedule = (void (*)(CFReadStreamRef, CFRunLoopRef, CFStringRef, void*))_FTPStreamUnschedule;
4847  			
4848              result = CFReadStreamCreate(alloc, (CFReadStreamCallBacks*)&_FTPReadStreamCallBacks, ctxt);
4849  		}
4850          
4851          if (result) {
4852  
4853              ctxt->_userStream = result;		// Don't retain for fear of loop.
4854              
4855              if (username) {
4856  		// the username in the ftpURL was validated above
4857  		CFReadStreamSetProperty(result, kCFStreamPropertyFTPUserName_prevalidated, username);
4858              }
4859  	    if (password) {
4860  		// the password in the ftpURL was validated above
4861  		CFReadStreamSetProperty(result, kCFStreamPropertyFTPPassword_prevalidated, password);
4862  	    }
4863          }
4864          else {
4865              
4866              if (ctxt->_url)
4867                  CFRelease(ctxt->_url);
4868  
4869              if (ctxt->_runloops)
4870                  CFRelease(ctxt->_runloops);
4871              
4872              if (ctxt->_properties)
4873                  CFRelease(ctxt->_properties);
4874  
4875              CFAllocatorDeallocate(alloc, ctxt);
4876          }
4877      }
4878  
4879      CFRelease(ftpURL);
4880      
4881      if (username) {
4882  	CFRelease(username);
4883      }
4884      if (password) {
4885  	CFRelease(password);
4886      }
4887      
4888      return result;
4889  }
4890  
4891  
4892  /* CF_EXPORT */ CFIndex
4893  CFFTPCreateParsedResourceListing(CFAllocatorRef alloc, const UInt8 *buffer, 
4894                                   CFIndex bufferLength, CFDictionaryRef *parsed)
4895  {
4896  	CFIndex totalConsumed;	// total characters consumed from buffer
4897  
4898      *parsed = NULL;
4899  	totalConsumed = 0;
4900  
4901      // Bail if a null or empty buffer.
4902      if ( (buffer != NULL) && (bufferLength != 0) ) {
4903  		const UInt8* scanStart;	// starting location to scan for line
4904  		CFIndex scanLength;		// length to scan for line
4905  		
4906  		scanStart = buffer;
4907  		scanLength = bufferLength;
4908  		do {
4909  			CFIndex consumed;	// number of characters consumed by _FindLine
4910  			const UInt8* first;	// if not NULL, the beginning of the line to parse
4911  			const UInt8* eol;	// if not NULL, the first EOL character after the line (more may have been consumed)
4912  			
4913  			/* find a line (if possible) and consume as many characters as possible */
4914  			consumed = _FindLine(scanStart, scanLength, &first, &eol);
4915  			totalConsumed += consumed;
4916  			scanStart += consumed;
4917  			scanLength -= consumed;
4918  			
4919  			if ( first == NULL ) {
4920  				/* a line was not found so break */
4921  				break;
4922  			}
4923  			
4924  			// If it's not the summary line, parse it.
4925  			if (memcmp("total ", first, 6)) {
4926  
4927  				int count = 0;
4928  				const UInt8* fields[16];
4929  
4930  				// This is an example of the intended target listing:
4931  				//    drwxrwxrwx  linkcount  user  group  size  month  day  yearOrTime  name
4932  
4933  				memset(fields, 0, sizeof(fields));
4934  
4935  				// Parse out each field.  If more than the number of fields
4936  				// are parsed, assume they're all part of the name.
4937  				while ((count < (sizeof(fields) / sizeof(fields[0]))) && (first < eol)) {
4938  
4939  					// Skip leading space
4940  					while ((first < eol) && isspace(*first))
4941  						first++;
4942  
4943  					// No more parsing if at the end of the line.
4944  					if (first >= eol)
4945  						break;
4946  
4947  					// Save the location of the field.
4948  					fields[count++] = first;
4949  
4950  					// Skip over the field.
4951  					while ((first < eol) && !isspace(*first))
4952  						first++;
4953  				}
4954  
4955  				// If nothing parsed see if the next line does.
4956  				if (count) {
4957  
4958  					int type, mode;
4959  					Boolean hadModeBits = TRUE;
4960  					Boolean foundSize = FALSE;
4961  
4962  					// Get the file type.
4963  					switch (fields[0][0]) {
4964  						case 'b': type = DT_BLK; break;		// Block special file.
4965  						case 'c': type = DT_CHR; break;		// Character special file.
4966  						case 'd': type = DT_DIR; break;		// Directory.
4967  						case 'l': type = DT_LNK; break;		// Symbolic link.
4968  						case 's': type = DT_SOCK; break;		// Socket link.
4969  						case 'p': type = DT_FIFO; break;		// FIFO.
4970  						case '-': type = DT_REG; break;		// Regular file.
4971  						default: type = DT_UNKNOWN; break;
4972  					}
4973  		
4974  					mode = 0;
4975  		
4976  					// Enough bytes to consider the mode field?
4977  					if ((eol - fields[0]) < 11)
4978  						hadModeBits = FALSE;
4979  					
4980  					else
4981  						hadModeBits = _ReadModeBits(&fields[0][1], &mode);
4982  					
4983  					// Continue establishing the other information if room.  Start with date as the next anchor.
4984  					if (fields[3] && fields[4]) {
4985  						
4986  						int i = 3;
4987  						UInt64 size = 0;
4988  						const UInt8* user = NULL;
4989  						const UInt8* group = NULL;
4990  						CFDateRef date = NULL;
4991  						
4992  						// Shoot to establish the next anchor, the date/time.
4993  						while (fields[i]) {
4994  							
4995  							// Try to get the date/time.
4996  							const UInt8* end = _CFFTPGetDateTimeFunc(alloc, fields[i], eol - fields[i], &date);
4997  							
4998  							// If built one, find out where it ended and the name begins.
4999  							if (date) {
5000  								
5001  								int j = i - 1;
5002  								
5003  								// Walk backwards from the date to find the size.
5004  								while (j >= 0) {
5005  									
5006  									// Allow "mode" field to be the size if no mode bits were there.
5007  									if (!j && hadModeBits)
5008  										break;
5009  									
5010  									// Try to convert to size.
5011  									if (_ReadSize(fields[j], &size)) {
5012  										
5013  										foundSize = TRUE;
5014  										
5015  										j--;	// Assume the previous field to be group.
5016  										
5017  										// If it's not the first field or the mode bits
5018  										// weren't in the first field, call it the group.
5019  										if (j || !hadModeBits) {
5020  											
5021  											group = fields[j];
5022  											
5023  											j--;	// Assume the previous field to be user.
5024  											
5025  											// If there is another field and it's not the
5026  											// mode bits, use it for the user field, otherwise
5027  											// assume the user and group were a single field.
5028  											if (!j && hadModeBits)
5029  												user = group;
5030  											
5031  											else {
5032  												
5033  												UInt64 linkcount = 0;
5034  												if (hadModeBits && (j == 1) && _ReadSize(fields[j], &linkcount))
5035  													user = group;
5036  												else
5037  													user = fields[j];
5038  											}
5039  										}
5040  										
5041  										// Found size so break out of here.
5042  										break;
5043  									}
5044  								}
5045  								
5046  								// Find out what the next field is.
5047  								while (fields[i] && (end > fields[i]))
5048  									i++;
5049  								break;
5050  							}
5051  							
5052  							// Try the next field as a date/time.
5053  							i++;
5054  						}
5055  						
5056  						// Found a date but is there a name field?
5057  						if (fields[i] && date) {
5058  							
5059  							int j = 0;
5060  							const UInt8* tmp = NULL;
5061  							const UInt8* name = fields[i];
5062  							const UInt8* link = NULL;
5063  							const void *keys[kResourceInfoItemCount], *values[kResourceInfoItemCount];
5064  							
5065  							// If it's a link, find the link information.
5066  							if (type == DT_LNK) {
5067  								
5068  								// Hunt for the "->" separator.
5069  								while (fields[i]) {
5070  									
5071  									// If found, save the link.
5072  									if (!memcmp(fields[i++], "->", 2)) {
5073  										link = fields[i];
5074  										break;
5075  									}
5076  								}
5077  							}
5078  							
5079  							// If there were mode bits, save them in the values.
5080  							if (hadModeBits) {
5081  								keys[j] = kCFFTPResourceMode;
5082  								values[j] = CFNumberCreate(alloc, kCFNumberSInt32Type, &mode);
5083  								if (values[j]) j++;
5084  							}
5085  							
5086  							// Save the name in the values.
5087  							keys[j] = kCFFTPResourceName;						
5088  							values[j] = CFStringCreateWithBytes(alloc, name, !link ? eol - name : (fields[i - 1] - 1) - name, kCFStringEncodingMacRoman, FALSE);
5089  							if (values[j]) {
5090  								CFStringRef temp = _CFStringCreateCopyWithStrippedHTML(alloc, values[j]);
5091  								if (temp) {
5092  									CFRelease(values[j]);
5093  									values[j] = temp;
5094  								}
5095  								j++;
5096  							}
5097  							
5098  							// If there was a link, save it.
5099  							if (link) {
5100  								keys[j]   = kCFFTPResourceLink;
5101  								values[j] = CFStringCreateWithBytes(alloc, link, eol - link, kCFStringEncodingMacRoman, FALSE);
5102  								if (values[j]) j++;
5103  							}
5104  							else {
5105  								keys[j]   = kCFFTPResourceLink;
5106  								values[j] = CFStringCreateWithCString(alloc, "", kCFStringEncodingUTF8);
5107  								if (values[j]) j++;
5108  							}
5109  							
5110  							// If the size was found, save the other bits.
5111  							if (foundSize) {
5112  								
5113  								if (group && (group == user)) {
5114  									
5115  									const char kUserGroupSeparators[] = {'|', ':', '/', '\\'};
5116  									const UInt8* sep = NULL;
5117  									const UInt8* end = user;
5118  									
5119  									while (!isspace(*end))
5120  										end++;
5121  									
5122  									for (i = 0; !sep && (i < (sizeof(kUserGroupSeparators) / sizeof(kUserGroupSeparators[0]))); i++)
5123  										sep = memchr(user, kUserGroupSeparators[i], end - user);
5124  									
5125  									if (sep) {
5126  										tmp = sep;		// Set tmp so length of user is properly calculated.
5127  										group = sep + 1;
5128  									}
5129  									
5130  									// NOTE that if no separator is found, user and group will get the
5131  									// same value.
5132  								}
5133  								
5134  								if (user) {
5135  									
5136  									if (!tmp) {
5137  										for (tmp = user; !isspace(*tmp); tmp++)
5138  											/* Do nothing. */ ;
5139  									}
5140  									
5141  									// Save the owner.  There is only one if a size was found.
5142  									keys[j]   = kCFFTPResourceOwner;
5143  									values[j] = CFStringCreateWithBytes(alloc, user, tmp - user, kCFStringEncodingMacRoman, FALSE);
5144  									if (values[j]) j++;
5145  								}
5146  								
5147  								if (group) {
5148  									
5149  									for (tmp = group; !isspace(*tmp); tmp++)
5150  										/* Do nothing. */ ;
5151  									
5152  									// Save the group.  There is only one if a size was found.
5153  									keys[j]   = kCFFTPResourceGroup;
5154  									values[j] = CFStringCreateWithBytes(alloc, group, tmp - group, kCFStringEncodingMacRoman, FALSE);
5155  									if (values[j]) j++;
5156  								}
5157  								
5158  								// Save the size.
5159  								keys[j]   = kCFFTPResourceSize;
5160  								values[j] = CFNumberCreate(alloc, kCFNumberLongLongType, &size);
5161  								if (values[j]) j++;
5162  							}
5163  							
5164  							// Save the file type.
5165  							keys[j]   = kCFFTPResourceType;
5166  							values[j] = CFNumberCreate(alloc, kCFNumberIntType, &type);
5167  							if (values[j]) j++;
5168  							
5169  							// Save the date.
5170  							keys[j]   = kCFFTPResourceModDate;
5171  							values[j++] = CFRetain(date);		// Extra retain because it's released twice.
5172  							
5173  							// Create the dictionary of information for the user.
5174  							*parsed = CFDictionaryCreate(alloc, keys, values, j, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
5175  							
5176  							// Release all the items that had been created.
5177  							for (--j; j >= 0; j--)
5178  								CFRelease(values[j]);
5179  							
5180  							// Did the parse, so bail.
5181  							break;
5182  						}
5183  						
5184  						// If date was allocated, release it.
5185  						if (date)
5186  							CFRelease(date);
5187  					}
5188  		
5189  					// No mode bits, so deal with only a name.
5190  					if (!hadModeBits) {
5191  						const void *keys[1], *values[1];
5192  
5193  						// Save the name in the values.
5194  						keys[0] = kCFFTPResourceName;
5195  						values[0] = CFStringCreateWithBytes(alloc, fields[0], eol - fields[0], kCFStringEncodingMacRoman, FALSE);
5196  						
5197  
5198  						// Create the dictionary of information for the user.
5199  						if (values[0]) {
5200  							
5201  							if (!CFStringHasPrefix(values[0], kHTMLTagOpen) || !CFStringHasSuffix(values[0], kHTMLTagClose)) {
5202  								
5203  								*parsed = CFDictionaryCreate(alloc, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
5204  								
5205  								CFRelease(values[0]);
5206  								
5207  								// Did the parse, so bail.
5208  								break;
5209  							}
5210  							
5211  							CFRelease(values[0]);
5212  						}
5213  					}
5214  				}
5215  			}
5216  			
5217  			// Bail if at the end or beyond.
5218  			if ( totalConsumed >= bufferLength ) {
5219  				break;
5220  			}
5221  			
5222  		} while (1);
5223  	}
5224  	
5225      // Return the number of bytes parsed.
5226      return ( totalConsumed );
5227  }