--- /dev/null
+/*
+ * Copyright (c) 2010-2011 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "Transform.h"
+#include "SecTransform.h"
+#include "SecCollectTransform.h"
+#include "SecCustomTransform.h"
+#include "misc.h"
+#include "c++utils.h"
+#include "Utilities.h"
+
+static CFStringRef kCollectTransformName = CFSTR("com.apple.security.seccollecttransform");
+
+static SecTransformInstanceBlock CollectTransform(CFStringRef name,
+ SecTransformRef newTransform,
+ SecTransformImplementationRef ref)
+{
+ SecTransformInstanceBlock instanceBlock =
+ ^{
+ __block CFMutableArrayRef allValues =
+ CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ __block Boolean isSameType = TRUE;
+ CFTypeRef input_ah = SecTranformCustomGetAttribute(ref, kSecTransformInputAttributeName, kSecTransformMetaAttributeRef);
+ ah2ta(input_ah)->direct_error_handling = 1;
+
+ dispatch_block_t no_more_output = ^
+ {
+ SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, input_ah, ^(SecTransformStringOrAttributeRef a, CFTypeRef v) { return v; });
+ };
+
+ // Create a block to deal with out of memory errors
+ dispatch_block_t oom = ^
+ {
+ CFTypeRefHolder localErr(GetNoMemoryError());
+ SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
+ kSecTransformMetaAttributeValue, localErr.Get());
+ no_more_output();
+ };
+
+ SecTransformSetTransformAction(ref, kSecTransformActionFinalize,
+ ^()
+ {
+ if (NULL != allValues)
+ {
+ CFRelease(allValues);
+ }
+
+ return (CFTypeRef) NULL;
+ });
+
+ SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification,
+ input_ah,
+ ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value)
+ {
+ CFIndex len = CFArrayGetCount(allValues);
+
+#if 0
+ if (NULL == value && 0 == len)
+ {
+ SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
+ kSecTransformMetaAttributeValue, NULL);
+ no_more_output();
+ return value;
+ }
+#endif
+
+ if (value && isSameType && len > 0)
+ {
+ isSameType = CFGetTypeID(CFArrayGetValueAtIndex(allValues, 0)) == CFGetTypeID(value);
+ }
+
+ if (value)
+ {
+
+ if (CFGetTypeID(value) == CFErrorGetTypeID())
+ {
+ SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
+ kSecTransformMetaAttributeValue, value);
+ no_more_output();
+ return value;
+ }
+
+ // For mutable types, we want an immutable copy.
+ /// XXX: write a more general CFImutableCopy and use it here.
+ if (CFGetTypeID(value) == CFDataGetTypeID())
+ {
+ CFDataRef copy = CFDataCreateCopy(NULL, (CFDataRef)value);
+
+ CFArrayAppendValue(allValues, copy);
+ CFRelease(copy);
+ }
+ else
+ {
+ CFArrayAppendValue(allValues, value);
+ }
+
+ if (CFArrayGetCount(allValues) != len +1)
+ {
+ oom();
+ return value;
+ }
+ }
+ else
+ {
+ if (isSameType)
+ {
+ // Deal with data or no items at all
+ CFTypeID type = CFArrayGetCount(allValues) ?
+ CFGetTypeID(CFArrayGetValueAtIndex(allValues, 0)) : CFDataGetTypeID();
+ if (CFDataGetTypeID() == type)
+ {
+ CFIndex total_len = 0;
+ CFIndex prev_total_len = 0;
+ CFIndex i;
+ const CFIndex n_datas = CFArrayGetCount(allValues);
+
+ for(i = 0; i < n_datas; i++)
+ {
+ total_len +=
+ CFDataGetLength((CFDataRef)CFArrayGetValueAtIndex(allValues, i));
+ if (total_len < prev_total_len)
+ {
+ oom();
+ return value;
+ }
+ prev_total_len = total_len;
+ }
+
+ CFMutableDataRef result = CFDataCreateMutable(NULL, total_len);
+ if (!result)
+ {
+ oom();
+ return value;
+ }
+
+ for(i = 0; i < n_datas; i++)
+ {
+ CFDataRef d = (CFDataRef)CFArrayGetValueAtIndex(allValues, i);
+ CFDataAppendBytes(result, CFDataGetBytePtr(d), CFDataGetLength(d));
+ }
+
+ if (CFDataGetLength(result) != total_len)
+ {
+ oom();
+ return value;
+ }
+
+ CFDataRef resultData = CFDataCreateCopy(NULL, result);
+
+ SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
+ kSecTransformMetaAttributeValue, (CFTypeRef)resultData);
+
+ CFRelease(resultData);
+
+ SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
+ kSecTransformMetaAttributeValue, (CFTypeRef)value);
+ no_more_output();
+
+ CFRelease(result);
+ return value;
+ }
+ else if (CFStringGetTypeID() == type)
+ {
+ // deal with strings
+ CFStringRef resultStr = CFStringCreateByCombiningStrings(NULL, allValues, CFSTR(""));
+
+ SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
+ kSecTransformMetaAttributeValue, (CFTypeRef)resultStr);
+ SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
+ kSecTransformMetaAttributeValue, (CFTypeRef)value);
+ no_more_output();
+
+ return value;
+ }
+ else
+ {
+ // special case the singleton
+ if (1 == CFArrayGetCount(allValues))
+ {
+ CFTypeRef result = (CFTypeRef)CFRetain(CFArrayGetValueAtIndex(allValues, 0));
+
+ SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
+ kSecTransformMetaAttributeValue, (CFTypeRef)result);
+ SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
+ kSecTransformMetaAttributeValue, (CFTypeRef)value);
+ no_more_output();
+
+ return value;
+ }
+ }
+ }
+ // Fall through for non-homogenous or un-mergable type
+ CFArrayRef resultArray = CFArrayCreateCopy(kCFAllocatorDefault, allValues);
+ SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
+ kSecTransformMetaAttributeValue, (CFTypeRef)resultArray);
+ SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
+ kSecTransformMetaAttributeValue, (CFTypeRef)value);
+ no_more_output();
+
+ return value;
+ }
+
+ return value;
+
+ });
+
+ return (CFErrorRef)NULL;
+ };
+
+ return Block_copy(instanceBlock);
+}
+
+SecTransformRef SecCreateCollectTransform(CFErrorRef* error)
+{
+ static dispatch_once_t once;
+ __block Boolean ok = TRUE;
+
+ dispatch_block_t aBlock = ^
+ {
+ ok = SecTransformRegister(kCollectTransformName, &CollectTransform, error);
+ };
+
+ dispatch_once(&once, aBlock);
+
+ if (!ok)
+ {
+ return NULL;
+ }
+
+ SecTransformRef yatz = SecTransformCreate(kCollectTransformName, error);
+ return yatz;
+}