]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
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; | |
427c49bc A |
44 | CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(array, 0); |
45 | ||
46 | // Ensure that indeed we do have a CFReadStreamRef | |
47 | if (NULL == item || CFReadStreamGetTypeID() != CFGetTypeID(item)) | |
b1ab9ed8 | 48 | { |
427c49bc | 49 | return (CFTypeRef) CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "The input attribute item was nil or not a read stream"); |
b1ab9ed8 A |
50 | } |
51 | ||
427c49bc A |
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 | ||
b1ab9ed8 A |
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 | ||
115 | SecTransformRef 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 | } |