/ OSX / libsecurity_transform / lib / SecTransformReadTransform.cpp
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  }