#include "SecTransformReadTransform.h" #include "SecCustomTransform.h" #include "Utilities.h" static CFStringRef kStreamTransformName = CFSTR("SecReadStreamTransform"); static CFStringRef kStreamMaxSize = CFSTR("MAX_READSIZE"); static SecTransformInstanceBlock StreamTransformImplementation(CFStringRef name, SecTransformRef newTransform, SecTransformImplementationRef ref) { SecTransformInstanceBlock instanceBlock = ^{ CFErrorRef result = NULL; if (NULL == name || NULL == newTransform) { } // define the storage for our block __block CFIndex blockSize = 4096; // make a default block size // it's not necessary to set the input stream size SecTransformCustomSetAttribute(ref, kStreamMaxSize, kSecTransformMetaAttributeRequired, kCFBooleanFalse); // define the action if we change the max read size SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kStreamMaxSize, ^(SecTransformAttributeRef attribute, CFTypeRef value) { CFNumberGetValue((CFNumberRef) value, kCFNumberCFIndexType, &blockSize); return value; }); // define for our input action SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, ^(SecTransformAttributeRef attribute, CFTypeRef value) { if (value == NULL) { return (CFTypeRef) NULL; } CFArrayRef array = (CFArrayRef) value; CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(array, 0); // Ensure that indeed we do have a CFReadStreamRef if (NULL == item || CFReadStreamGetTypeID() != CFGetTypeID(item)) { return (CFTypeRef) CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "The input attribute item was nil or not a read stream"); } // This now is a safe cast CFReadStreamRef input = (CFReadStreamRef)item; // Get the state of the stream CFStreamStatus streamStatus = CFReadStreamGetStatus(input); switch (streamStatus) { case kCFStreamStatusNotOpen: { if (!CFReadStreamOpen(input)) { // We didn't open properly. Error out return (CFTypeRef) CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "An error occurred while opening the stream."); } } break; case kCFStreamStatusError: { return (CFTypeRef) CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "The read stream is in an error state"); } break; default: // The assumption is that the stream is ready to go as is. break; } // allocate the read buffer on the heap u_int8_t* buffer = (u_int8_t*) malloc(blockSize); CFIndex bytesRead; bytesRead = CFReadStreamRead(input, buffer, blockSize); while (bytesRead > 0) { // make data from what was read CFDataRef value = CFDataCreate(NULL, buffer, bytesRead); // send it down the chain SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, value); // cleanup CFRelease(value); bytesRead = CFReadStreamRead(input, buffer, blockSize); } free(buffer); SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, (CFTypeRef) NULL); return (CFTypeRef) NULL; }); return result; }; return Block_copy(instanceBlock); } SecTransformRef SecTransformCreateReadTransformWithReadStream(CFReadStreamRef inputStream) { static dispatch_once_t once = 0; __block bool ok = true; __block CFErrorRef result = NULL; dispatch_once(&once, ^{ ok = SecTransformRegister(kStreamTransformName, &StreamTransformImplementation, &result); }); if (!ok) { return result; } else { SecTransformRef transform = SecTransformCreate(kStreamTransformName, &result); if (NULL != transform) { // if we add the read stream directly to the transform the internal source stream // will take over. This is bad. Instead, we wrap this in a CFArray so that we can // pass through undetected CFTypeRef arrayData[] = {inputStream}; CFArrayRef arrayRef = CFArrayCreate(NULL, arrayData, 1, &kCFTypeArrayCallBacks); // add the input to the transform SecTransformSetAttribute(transform, kSecTransformInputAttributeName, arrayRef, &result); CFRelease(arrayRef); } return transform; } }