/ OSX / libsecurity_transform / lib / SecCollectTransform.cpp
SecCollectTransform.cpp
  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  
 32  static CFStringRef kCollectTransformName = CFSTR("com.apple.security.seccollecttransform");
 33  
 34  static 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  						CFReleaseNull(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  						CFReleaseNull(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                                  CFReleaseNull(result);
166  								return value;
167  							}
168  
169  							CFDataRef resultData = CFDataCreateCopy(NULL, result);
170  							
171  							SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 
172  									kSecTransformMetaAttributeValue, (CFTypeRef)resultData);
173  									
174  							CFReleaseNull(resultData);
175  							
176  							SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 
177  									kSecTransformMetaAttributeValue, (CFTypeRef)value);
178  							no_more_output();
179  
180  							CFReleaseNull(result);
181  							return value;
182  						} 
183  						else if (CFStringGetTypeID() == type) 
184  						{
185  							// deal with strings
186  							CFStringRef resultStr = CFStringCreateByCombiningStrings(NULL, allValues, CFSTR(""));
187  							
188  							SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 
189  									kSecTransformMetaAttributeValue, (CFTypeRef)resultStr);
190  							SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 
191  									kSecTransformMetaAttributeValue, (CFTypeRef)value);
192  							no_more_output();
193                              CFReleaseNull(resultStr);
194  		
195  							return value;
196  						} 
197  						else 
198  						{
199  							// special case the singleton
200  							if (1 == CFArrayGetCount(allValues))
201  							{
202  								CFTypeRef result =  (CFTypeRef)CFRetainSafe(CFArrayGetValueAtIndex(allValues, 0));
203  								
204  								SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 
205  										kSecTransformMetaAttributeValue, (CFTypeRef)result);
206  								SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 
207  										kSecTransformMetaAttributeValue, (CFTypeRef)value);
208  								no_more_output();
209                                  CFReleaseNull(result);
210  
211  								return value;
212  							}
213  						}
214  					}
215  					// Fall through for non-homogenous or un-mergable type
216  					CFArrayRef resultArray = CFArrayCreateCopy(kCFAllocatorDefault, allValues);
217  					SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 
218  							kSecTransformMetaAttributeValue, (CFTypeRef)resultArray);
219  					SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 
220  							kSecTransformMetaAttributeValue, (CFTypeRef)value);
221  					no_more_output();
222                      CFReleaseNull(resultArray);
223  
224  					return value;
225  				}
226  
227  				return value;
228  
229  			});
230  										
231  		return (CFErrorRef)NULL;
232  	};
233  	
234  	return Block_copy(instanceBlock);	
235  }
236  
237  SecTransformRef SecCreateCollectTransform(CFErrorRef* error) 
238  {
239  	static dispatch_once_t once;
240  	__block Boolean ok = TRUE;
241  			
242  	dispatch_block_t aBlock = ^
243  	{
244  		ok = SecTransformRegister(kCollectTransformName, &CollectTransform, error);
245  	};
246  	
247  	dispatch_once(&once, aBlock);
248  
249  	if (!ok) 
250  	{
251  		return NULL;
252  	}
253  
254  	SecTransformRef yatz = SecTransformCreate(kCollectTransformName, error);
255  	return yatz;
256  }