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