1/*
2 * Copyright (c) 2010-2011 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "Transform.h"
25#include "SecTransform.h"
26#include "SecCollectTransform.h"
27#include "SecCustomTransform.h"
28#include "misc.h"
29#include "c++utils.h"
30#include "Utilities.h"
31
32static CFStringRef kCollectTransformName = CFSTR("com.apple.security.seccollecttransform");
33
34static SecTransformInstanceBlock CollectTransform(CFStringRef name,
35							SecTransformRef newTransform,
36							SecTransformImplementationRef ref)
37{
38	SecTransformInstanceBlock instanceBlock =
39	^{
40		__block CFMutableArrayRef allValues =
41			CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
42		__block Boolean isSameType = TRUE;
43		CFTypeRef input_ah = SecTranformCustomGetAttribute(ref, kSecTransformInputAttributeName, kSecTransformMetaAttributeRef);
44		ah2ta(input_ah)->direct_error_handling = 1;
45
46		dispatch_block_t no_more_output = ^
47		{
48			SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, input_ah, ^(SecTransformStringOrAttributeRef a, CFTypeRef v) { return v; });
49		};
50
51		// Create a block to deal with out of memory errors
52		dispatch_block_t oom = ^
53		{
54			CFTypeRefHolder localErr(GetNoMemoryError());
55			SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
56					kSecTransformMetaAttributeValue, localErr.Get());
57			no_more_output();
58		};
59
60		SecTransformSetTransformAction(ref, kSecTransformActionFinalize,
61			^()
62				{
63					if (NULL != allValues)
64					{
65						CFRelease(allValues);
66					}
67
68					return (CFTypeRef) NULL;
69				});
70
71		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification,
72			input_ah,
73			^(SecTransformStringOrAttributeRef attribute, CFTypeRef value)
74			{
75				CFIndex len = CFArrayGetCount(allValues);
76
77#if 0
78				if (NULL == value && 0 == len)
79				{
80					SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
81					kSecTransformMetaAttributeValue, NULL);
82					no_more_output();
83					return value;
84				}
85#endif
86
87				if (value && isSameType && len > 0)
88				{
89					isSameType = CFGetTypeID(CFArrayGetValueAtIndex(allValues, 0)) == CFGetTypeID(value);
90				}
91
92				if (value)
93				{
94
95					if (CFGetTypeID(value) == CFErrorGetTypeID())
96					{
97						SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
98								kSecTransformMetaAttributeValue, value);
99						no_more_output();
100						return value;
101					}
102
103					// For mutable types, we want an immutable copy.
104					/// XXX: write a more general CFImutableCopy and use it here.
105					if (CFGetTypeID(value) == CFDataGetTypeID())
106					{
107						CFDataRef copy = CFDataCreateCopy(NULL, (CFDataRef)value);
108
109						CFArrayAppendValue(allValues, copy);
110						CFRelease(copy);
111					}
112					else
113					{
114						CFArrayAppendValue(allValues, value);
115					}
116
117					if (CFArrayGetCount(allValues) != len +1)
118					{
119						oom();
120						return value;
121					}
122				}
123				else
124				{
125					if (isSameType)
126					{
127						// Deal with data or no items at all
128						CFTypeID type = CFArrayGetCount(allValues) ?
129							CFGetTypeID(CFArrayGetValueAtIndex(allValues, 0)) : CFDataGetTypeID();
130						if (CFDataGetTypeID() == type)
131						{
132							CFIndex total_len = 0;
133							CFIndex prev_total_len = 0;
134							CFIndex i;
135							const CFIndex n_datas = CFArrayGetCount(allValues);
136
137							for(i = 0; i < n_datas; i++)
138							{
139								total_len +=
140									CFDataGetLength((CFDataRef)CFArrayGetValueAtIndex(allValues, i));
141								if (total_len < prev_total_len)
142								{
143									oom();
144									return value;
145								}
146								prev_total_len = total_len;
147							}
148
149							CFMutableDataRef result = CFDataCreateMutable(NULL, total_len);
150							if (!result)
151							{
152								oom();
153								return value;
154							}
155
156							for(i = 0; i < n_datas; i++)
157							{
158								CFDataRef d = (CFDataRef)CFArrayGetValueAtIndex(allValues, i);
159								CFDataAppendBytes(result, CFDataGetBytePtr(d), CFDataGetLength(d));
160							}
161
162							if (CFDataGetLength(result) != total_len)
163							{
164								oom();
165								return value;
166							}
167
168							CFDataRef resultData = CFDataCreateCopy(NULL, result);
169
170							SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
171									kSecTransformMetaAttributeValue, (CFTypeRef)resultData);
172
173							CFRelease(resultData);
174
175							SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
176									kSecTransformMetaAttributeValue, (CFTypeRef)value);
177							no_more_output();
178
179							CFRelease(result);
180							return value;
181						}
182						else if (CFStringGetTypeID() == type)
183						{
184							// deal with strings
185							CFStringRef resultStr = CFStringCreateByCombiningStrings(NULL, allValues, CFSTR(""));
186
187							SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
188									kSecTransformMetaAttributeValue, (CFTypeRef)resultStr);
189							SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
190									kSecTransformMetaAttributeValue, (CFTypeRef)value);
191							no_more_output();
192
193							return value;
194						}
195						else
196						{
197							// special case the singleton
198							if (1 == CFArrayGetCount(allValues))
199							{
200								CFTypeRef result =  (CFTypeRef)CFRetain(CFArrayGetValueAtIndex(allValues, 0));
201
202								SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
203										kSecTransformMetaAttributeValue, (CFTypeRef)result);
204								SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
205										kSecTransformMetaAttributeValue, (CFTypeRef)value);
206								no_more_output();
207
208								return value;
209							}
210						}
211					}
212					// Fall through for non-homogenous or un-mergable type
213					CFArrayRef resultArray = CFArrayCreateCopy(kCFAllocatorDefault, allValues);
214					SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
215							kSecTransformMetaAttributeValue, (CFTypeRef)resultArray);
216					SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
217							kSecTransformMetaAttributeValue, (CFTypeRef)value);
218					no_more_output();
219
220					return value;
221				}
222
223				return value;
224
225			});
226
227		return (CFErrorRef)NULL;
228	};
229
230	return Block_copy(instanceBlock);
231}
232
233SecTransformRef SecCreateCollectTransform(CFErrorRef* error)
234{
235	static dispatch_once_t once;
236	__block Boolean ok = TRUE;
237
238	dispatch_block_t aBlock = ^
239	{
240		ok = SecTransformRegister(kCollectTransformName, &CollectTransform, error);
241	};
242
243	dispatch_once(&once, aBlock);
244
245	if (!ok)
246	{
247		return NULL;
248	}
249
250	SecTransformRef yatz = SecTransformCreate(kCollectTransformName, error);
251	return yatz;
252}
253