--- /dev/null
+#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;
+ }
+}