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 }