SecTransformReadTransform.cpp
1 #include "SecTransformReadTransform.h" 2 #include "SecCustomTransform.h" 3 #include "Utilities.h" 4 5 static CFStringRef kStreamTransformName = CFSTR("SecReadStreamTransform"); 6 static CFStringRef kStreamMaxSize = CFSTR("MAX_READSIZE"); 7 8 static 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 74 default: 75 // The assumption is that the stream is ready to go as is. 76 break; 77 } 78 79 // allocate the read buffer on the heap 80 u_int8_t* buffer = (u_int8_t*) malloc(blockSize); 81 82 CFIndex bytesRead; 83 84 bytesRead = CFReadStreamRead(input, buffer, blockSize); 85 while (bytesRead > 0) 86 { 87 // make data from what was read 88 CFDataRef value = CFDataCreate(NULL, buffer, bytesRead); 89 90 // send it down the chain 91 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, value); 92 93 // cleanup 94 CFReleaseNull(value); 95 96 bytesRead = CFReadStreamRead(input, buffer, blockSize); 97 } 98 99 free(buffer); 100 101 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, (CFTypeRef) NULL); 102 103 return (CFTypeRef) NULL; 104 }); 105 106 return result; 107 }; 108 109 return Block_copy(instanceBlock); 110 } 111 112 113 114 SecTransformRef SecTransformCreateReadTransformWithReadStream(CFReadStreamRef inputStream) 115 { 116 static dispatch_once_t once = 0; 117 118 __block bool ok = true; 119 __block CFErrorRef result = NULL; 120 121 dispatch_once(&once, 122 ^{ 123 ok = SecTransformRegister(kStreamTransformName, &StreamTransformImplementation, &result); 124 }); 125 126 if (!ok) 127 { 128 return result; 129 } 130 else 131 { 132 133 SecTransformRef transform = SecTransformCreate(kStreamTransformName, &result); 134 if (NULL != transform) 135 { 136 // if we add the read stream directly to the transform the internal source stream 137 // will take over. This is bad. Instead, we wrap this in a CFArray so that we can 138 // pass through undetected 139 CFTypeRef arrayData[] = {inputStream}; 140 CFArrayRef arrayRef = CFArrayCreate(NULL, arrayData, 1, &kCFTypeArrayCallBacks); 141 142 // add the input to the transform 143 SecTransformSetAttribute(transform, kSecTransformInputAttributeName, arrayRef, &result); 144 145 CFReleaseNull(arrayRef); 146 } 147 148 return transform; 149 } 150 }