1#include "SecTransformReadTransform.h" 2#include "SecCustomTransform.h" 3#include "Utilities.h" 4 5static CFStringRef kStreamTransformName = CFSTR("SecReadStreamTransform"); 6static CFStringRef kStreamMaxSize = CFSTR("MAX_READSIZE"); 7 8static SecTransformInstanceBlock StreamTransformImplementation(CFStringRef name, 9 SecTransformRef newTransform, 10 SecTransformImplementationRef ref) 11{ 12 SecTransformInstanceBlock instanceBlock = 13 ^{ 14 CFErrorRef result = NULL; 15 16 if (NULL == name || NULL == newTransform) 17 { 18 } 19 20 // define the storage for our block 21 __block CFIndex blockSize = 4096; // make a default block size 22 23 // it's not necessary to set the input stream size 24 SecTransformCustomSetAttribute(ref, kStreamMaxSize, kSecTransformMetaAttributeRequired, kCFBooleanFalse); 25 26 // define the action if we change the max read size 27 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kStreamMaxSize, 28 ^(SecTransformAttributeRef attribute, CFTypeRef value) 29 { 30 CFNumberGetValue((CFNumberRef) value, kCFNumberCFIndexType, &blockSize); 31 return value; 32 }); 33 34 // define for our input action 35 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, 36 ^(SecTransformAttributeRef attribute, CFTypeRef value) 37 { 38 if (value == NULL) 39 { 40 return (CFTypeRef) NULL; 41 } 42 43 CFArrayRef array = (CFArrayRef) value; 44 CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(array, 0); 45 46 // Ensure that indeed we do have a CFReadStreamRef 47 if (NULL == item || CFReadStreamGetTypeID() != CFGetTypeID(item)) 48 { 49 return (CFTypeRef) CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "The input attribute item was nil or not a read stream"); 50 } 51 52 // This now is a safe cast 53 CFReadStreamRef input = (CFReadStreamRef)item; 54 55 // Get the state of the stream 56 CFStreamStatus streamStatus = CFReadStreamGetStatus(input); 57 switch (streamStatus) 58 { 59 case kCFStreamStatusNotOpen: 60 { 61 if (!CFReadStreamOpen(input)) 62 { 63 // We didn't open properly. Error out 64 return (CFTypeRef) CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "An error occurred while opening the stream."); 65 } 66 } 67 break; 68 69 case kCFStreamStatusError: 70 { 71 return (CFTypeRef) CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "The read stream is in an error state"); 72 } 73 break; 74 75 default: 76 // The assumption is that the stream is ready to go as is. 77 break; 78 } 79 80 // allocate the read buffer on the heap 81 u_int8_t* buffer = (u_int8_t*) malloc(blockSize); 82 83 CFIndex bytesRead; 84 85 bytesRead = CFReadStreamRead(input, buffer, blockSize); 86 while (bytesRead > 0) 87 { 88 // make data from what was read 89 CFDataRef value = CFDataCreate(NULL, buffer, bytesRead); 90 91 // send it down the chain 92 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, value); 93 94 // cleanup 95 CFRelease(value); 96 97 bytesRead = CFReadStreamRead(input, buffer, blockSize); 98 } 99 100 free(buffer); 101 102 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, (CFTypeRef) NULL); 103 104 return (CFTypeRef) NULL; 105 }); 106 107 return result; 108 }; 109 110 return Block_copy(instanceBlock); 111} 112 113 114 115SecTransformRef SecTransformCreateReadTransformWithReadStream(CFReadStreamRef inputStream) 116{ 117 static dispatch_once_t once = 0; 118 119 __block bool ok = true; 120 __block CFErrorRef result = NULL; 121 122 dispatch_once(&once, 123 ^{ 124 ok = SecTransformRegister(kStreamTransformName, &StreamTransformImplementation, &result); 125 }); 126 127 if (!ok) 128 { 129 return result; 130 } 131 else 132 { 133 134 SecTransformRef transform = SecTransformCreate(kStreamTransformName, &result); 135 if (NULL != transform) 136 { 137 // if we add the read stream directly to the transform the internal source stream 138 // will take over. This is bad. Instead, we wrap this in a CFArray so that we can 139 // pass through undetected 140 CFTypeRef arrayData[] = {inputStream}; 141 CFArrayRef arrayRef = CFArrayCreate(NULL, arrayData, 1, &kCFTypeArrayCallBacks); 142 143 // add the input to the transform 144 SecTransformSetAttribute(transform, kSecTransformInputAttributeName, arrayRef, &result); 145 146 CFRelease(arrayRef); 147 } 148 149 return transform; 150 } 151} 152