static int wait_runloop(mailstream_low * s, int wait_state) { struct mailstream_cfstream_data * cfstream_data; int read_scheduled; int write_scheduled; int error; setup_runloop(s); cfstream_data = (struct mailstream_cfstream_data *) s->data; cfstream_data->state = wait_state; read_scheduled = 0; write_scheduled = 0; error = WAIT_RUNLOOP_EXIT_NO_ERROR; switch (wait_state) { case STATE_WAIT_OPEN: //fprintf(stderr, "wait open\n"); CFReadStreamScheduleWithRunLoop(cfstream_data->readStream, cfstream_data->runloop, kCFRunLoopDefaultMode); CFWriteStreamScheduleWithRunLoop(cfstream_data->writeStream, cfstream_data->runloop, kCFRunLoopDefaultMode); read_scheduled = 1; write_scheduled = 1; break; case STATE_WAIT_READ: //fprintf(stderr, "wait read\n"); CFReadStreamScheduleWithRunLoop(cfstream_data->readStream, cfstream_data->runloop, kCFRunLoopDefaultMode); read_scheduled = 1; break; case STATE_WAIT_WRITE: //fprintf(stderr, "wait write\n"); CFWriteStreamScheduleWithRunLoop(cfstream_data->writeStream, cfstream_data->runloop, kCFRunLoopDefaultMode); write_scheduled = 1; break; case STATE_WAIT_IDLE: //fprintf(stderr, "wait idle\n"); CFReadStreamScheduleWithRunLoop(cfstream_data->readStream, cfstream_data->runloop, kCFRunLoopDefaultMode); read_scheduled = 1; break; case STATE_WAIT_SSL: //fprintf(stderr, "wait ssl\n"); CFReadStreamScheduleWithRunLoop(cfstream_data->readStream, cfstream_data->runloop, kCFRunLoopDefaultMode); CFWriteStreamScheduleWithRunLoop(cfstream_data->writeStream, cfstream_data->runloop, kCFRunLoopDefaultMode); read_scheduled = 1; write_scheduled = 1; break; } if (read_scheduled) { if (CFReadStreamHasBytesAvailable(cfstream_data->readStream)) { readStreamCallback(cfstream_data->readStream, kCFStreamEventHasBytesAvailable, s); } } if (write_scheduled) { if (CFWriteStreamCanAcceptBytes(cfstream_data->writeStream)) { writeStreamCallback(cfstream_data->writeStream, kCFStreamEventCanAcceptBytes, s); } } while (1) { struct timeval timeout; CFTimeInterval delay; int r; int done; if (cfstream_data->cancelled) { error = WAIT_RUNLOOP_EXIT_CANCELLED; break; } if (cfstream_data->state == STATE_WAIT_IDLE) { if (cfstream_data->idleInterrupted) { error = WAIT_RUNLOOP_EXIT_INTERRUPTED; break; } } done = 0; switch (cfstream_data->state) { case STATE_OPEN_READ_DONE: CFReadStreamUnscheduleFromRunLoop(cfstream_data->readStream, cfstream_data->runloop, kCFRunLoopDefaultMode); read_scheduled = 0; break; case STATE_OPEN_WRITE_DONE: CFWriteStreamUnscheduleFromRunLoop(cfstream_data->writeStream, cfstream_data->runloop, kCFRunLoopDefaultMode); write_scheduled = 0; break; case STATE_OPEN_READ_WRITE_DONE: done = 1; break; case STATE_OPEN_WRITE_READ_DONE: done = 1; break; case STATE_READ_DONE: done = 1; break; case STATE_WRITE_DONE: done = 1; break; case STATE_IDLE_DONE: done = 1; break; case STATE_SSL_READ_DONE: done = 1; break; case STATE_SSL_WRITE_DONE: done = 1; break; case STATE_SSL_READ_WRITE_DONE: done = 1; break; case STATE_SSL_WRITE_READ_DONE: done = 1; break; } if (done) { break; } if (wait_state == STATE_WAIT_IDLE) { timeout.tv_sec = cfstream_data->idleMaxDelay; timeout.tv_usec = 0; } else { if (s->timeout == 0) { timeout = mailstream_network_delay; } else { timeout.tv_sec = s->timeout; timeout.tv_usec = 0; } } delay = (CFTimeInterval) timeout.tv_sec + (CFTimeInterval) timeout.tv_usec / (CFTimeInterval) 1e6; r = CFRunLoopRunInMode(kCFRunLoopDefaultMode, delay, true); if (r == kCFRunLoopRunTimedOut) { error = WAIT_RUNLOOP_EXIT_TIMEOUT; break; } } if (read_scheduled) { CFReadStreamUnscheduleFromRunLoop(cfstream_data->readStream, cfstream_data->runloop, kCFRunLoopDefaultMode); } if (write_scheduled) { CFWriteStreamUnscheduleFromRunLoop(cfstream_data->writeStream, cfstream_data->runloop, kCFRunLoopDefaultMode); } unsetup_runloop(s); if (error != WAIT_RUNLOOP_EXIT_NO_ERROR) return error; return WAIT_RUNLOOP_EXIT_NO_ERROR; }
/* extern */ Boolean HttpContextOpen(HttpContextRef context) { do { Boolean didSet; CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFStreamClientContext streamCtxt = {0, context, (void*(*)(void*))&HttpContextRetain, (void(*)(void*))&HttpContextRelease, NULL}; CFRunLoopTimerContext timerCtxt = {0, context, (const void*(*)(const void*))&HttpContextRetain, (void(*)(const void*))&HttpContextRelease, NULL}; // Set the client on the read stream. didSet = CFReadStreamSetClient(context->_inStream, kReadEvents, (CFReadStreamClientCallBack)&_ReadStreamCallBack, &streamCtxt); // Fail if unable to set the client. if (!didSet) break; // Set the client on the write stream. didSet = CFWriteStreamSetClient(context->_outStream, kWriteEvents, (CFWriteStreamClientCallBack)&_WriteStreamCallBack, &streamCtxt); // Fail if unable to set the client. if (!didSet) break; // Schedule the streams on the current run loop and default mode. CFReadStreamScheduleWithRunLoop(context->_inStream, runLoop, kCFRunLoopCommonModes); CFWriteStreamScheduleWithRunLoop(context->_outStream, runLoop, kCFRunLoopCommonModes); // Open the stream for reading. if (!CFReadStreamOpen(context->_inStream)) break; // Open the stream for writing. if (!CFWriteStreamOpen(context->_outStream)) break; // Create the timeout timer context->_timer = CFRunLoopTimerCreate(CFGetAllocator(context->_inStream), CFAbsoluteTimeGetCurrent() + kTimeOutInSeconds, 0, // interval 0, // flags 0, // order (CFRunLoopTimerCallBack)_TimerCallBack, &timerCtxt); // Fail if unable to create the timer. if (context->_timer == NULL) break; CFRunLoopAddTimer(runLoop, context->_timer, kCFRunLoopCommonModes); return TRUE; } while (0); // Something failed, so clean up. HttpContextClose(context); return FALSE; }
int tnet_transport_wrap(tnet_transport_t *transport, int index) { transport_context_t *context = transport->context; transport_socket_t *sock = context->sockets[index]; // If the socket is already wrapped in a CFSocket then return. if (sock->cf_socket || sock->cf_read_stream) { return 1; } // Put a reference to the transport context const CFSocketContext socket_context = { 0, transport, NULL, NULL, NULL }; if (TNET_SOCKET_TYPE_IS_DGRAM(sock->type)) { // Create a CFSocket from the native socket and register for Read events sock->cf_socket = CFSocketCreateWithNative(kCFAllocatorDefault, sock->fd, kCFSocketReadCallBack, &__CFSocketCallBack, &socket_context); // Don't close the socket if the CFSocket is invalidated CFOptionFlags flags = CFSocketGetSocketFlags(sock->cf_socket); flags = flags & ~kCFSocketCloseOnInvalidate; CFSocketSetSocketFlags(sock->cf_socket, flags); // Create a new RunLoopSource and register it with the main thread RunLoop sock->cf_run_loop_source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sock->cf_socket, 0); CFRunLoopAddSource(context->cf_run_loop, sock->cf_run_loop_source, kCFRunLoopDefaultMode); CFRelease(sock->cf_run_loop_source); } else if (TNET_SOCKET_TYPE_IS_STREAM(sock->type)) { // Create a pair of streams (read/write) from the socket CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock->fd, &sock->cf_read_stream, &sock->cf_write_stream); // Don't close underlying socket CFReadStreamSetProperty(sock->cf_read_stream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); CFWriteStreamSetProperty(sock->cf_write_stream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); if (TNET_SOCKET_TYPE_IS_SECURE(sock->type)) { CFMutableDictionaryRef settings = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionaryAddValue(settings, kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue); CFDictionaryAddValue(settings, kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue); CFDictionaryAddValue(settings, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse); CFDictionaryAddValue(settings, kCFStreamSSLPeerName, kCFNull); // Set the SSL settings CFReadStreamSetProperty(sock->cf_read_stream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL); CFReadStreamSetProperty(sock->cf_read_stream, kCFStreamPropertySSLSettings, settings); CFWriteStreamSetProperty(sock->cf_write_stream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL); CFWriteStreamSetProperty(sock->cf_write_stream, kCFStreamPropertySSLSettings, settings); CFRelease(settings); } #if __IPHONE_4_0 // Mark the stream for VoIP usage CFReadStreamSetProperty(sock->cf_read_stream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); CFWriteStreamSetProperty(sock->cf_write_stream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); #endif // Setup a context for the streams CFStreamClientContext streamContext = { 0, transport, NULL, NULL, NULL }; // Set the client callback for the stream CFReadStreamSetClient(sock->cf_read_stream, kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, &__CFReadStreamClientCallBack, &streamContext); CFWriteStreamSetClient(sock->cf_write_stream, kCFStreamEventOpenCompleted | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, &__CFWriteStreamClientCallBack, &streamContext); // Enroll streams in the run-loop CFReadStreamScheduleWithRunLoop(sock->cf_read_stream, context->cf_run_loop, kCFRunLoopDefaultMode); CFWriteStreamScheduleWithRunLoop(sock->cf_write_stream, context->cf_run_loop, kCFRunLoopDefaultMode); // Release references CFRelease(sock->cf_read_stream); CFRelease(sock->cf_write_stream); CFReadStreamOpen(sock->cf_read_stream); CFWriteStreamOpen(sock->cf_write_stream); } return 0; }
/* MySimpleDownload implements the download command. It sets up a MyStreamInfo 'object' with the read stream being an FTP stream of the file to download and the write stream being a file stream of the destination file. It then returns, and the real work happens asynchronously in the runloop. The function returns true if the stream setup succeeded, and false if it failed. */ static Boolean MySimpleDownload(CFStringRef urlString, CFURLRef destinationFolder, CFStringRef username, CFStringRef password) { CFReadStreamRef readStream; CFWriteStreamRef writeStream; CFStreamClientContext context = { 0, NULL, NULL, NULL, NULL }; CFURLRef downloadPath, downloadURL; CFStringRef fileName; Boolean dirPath, success = true; MyStreamInfo *streamInfo; assert(urlString != NULL); assert(destinationFolder != NULL); assert( (username != NULL) || (password == NULL) ); /* Returns true if the CFURL path represents a directory. */ dirPath = CFURLHasDirectoryPath(destinationFolder); if (!dirPath) { fprintf(stderr, "Download destination must be a directory.\n"); return false; } /* Create a CFURL from the urlString. */ downloadURL = CFURLCreateWithString(kCFAllocatorDefault, urlString, NULL); assert(downloadURL != NULL); /* Copy the end of the file path and use it as the file name. */ fileName = CFURLCopyLastPathComponent(downloadURL); assert(fileName != NULL); /* Create the downloadPath by taking the destination folder and appending the file name. */ downloadPath = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, destinationFolder, fileName, false); assert(downloadPath != NULL); CFRelease(fileName); /* Create a CFWriteStream for the file being downloaded. */ writeStream = CFWriteStreamCreateWithFile(kCFAllocatorDefault, downloadPath); assert(writeStream != NULL); CFRelease(downloadPath); /* CFReadStreamCreateWithFTPURL creates an FTP read stream for downloading from an FTP URL. */ readStream = CFReadStreamCreateWithFTPURL(kCFAllocatorDefault, downloadURL); assert(readStream != NULL); CFRelease(downloadURL); /* Initialize our MyStreamInfo structure, which we use to store some information about the stream. */ MyStreamInfoCreate(&streamInfo, readStream, writeStream); context.info = (void *)streamInfo; /* CFWriteStreamOpen will return success/failure. Opening a stream causes it to reserve all the system resources it requires. If the stream can open non-blocking, this will always return TRUE; listen to the run loop source to find out when the open completes and whether it was successful. */ success = CFWriteStreamOpen(writeStream); if (success) { /* CFReadStreamSetClient registers a callback to hear about interesting events that occur on a stream. */ success = CFReadStreamSetClient(readStream, kNetworkEvents, MyDownloadCallBack, &context); if (success) { /* Schedule a run loop on which the client can be notified about stream events. The client callback will be triggered via the run loop. It's the caller's responsibility to ensure that the run loop is running. */ CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); MyCFStreamSetUsernamePassword(readStream, username, password); MyCFStreamSetFTPProxy(readStream, &streamInfo->proxyDict); /* Setting the kCFStreamPropertyFTPFetchResourceInfo property will instruct the FTP stream to fetch the file size before downloading the file. Note that fetching the file size adds some time to the length of the download. Fetching the file size allows you to potentially provide a progress dialog during the download operation. You will retrieve the actual file size after your CFReadStream Callback gets called with a kCFStreamEventOpenCompleted event. */ CFReadStreamSetProperty(readStream, kCFStreamPropertyFTPFetchResourceInfo, kCFBooleanTrue); /* CFReadStreamOpen will return success/failure. Opening a stream causes it to reserve all the system resources it requires. If the stream can open non-blocking, this will always return TRUE; listen to the run loop source to find out when the open completes and whether it was successful. */ success = CFReadStreamOpen(readStream); if (success == false) { fprintf(stderr, "CFReadStreamOpen failed\n"); MyStreamInfoDestroy(streamInfo); } } else { fprintf(stderr, "CFReadStreamSetClient failed\n"); MyStreamInfoDestroy(streamInfo); } } else { fprintf(stderr, "CFWriteStreamOpen failed\n"); MyStreamInfoDestroy(streamInfo); } return success; }