#include <CoreServices/CoreServices.h>
#include <Block.h>
#include <libkern/OSAtomic.h>
#include <syslog.h>
#include "Transform.h"
#include "StreamSource.h"
#include "SingleShotSource.h"
#include "Monitor.h"
#include "Utilities.h"
#include "c++utils.h"
#include "misc.h"
#include "SecTransformInternal.h"
#include "GroupTransform.h"
#include "GroupTransform.h"
#include <pthread.h>
static const int kMaxPendingTransactions = 20;
static CFTypeID internalID = _kCFRuntimeNotATypeID;
static unsigned char dispatchQueueToTransformKey;
static char RandomChar()
{
return arc4random() % 26 + 'A'; }
static CFStringRef ah_set_describe(const void *v) {
transform_attribute *ta = ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v)));
return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@=%@ (conn: %@)"), ta->transform->GetName(), ta->name, ta->value ? ta->value : CFSTR("NULL"), ta->connections ? static_cast<CFTypeRef>(ta->connections) : static_cast<CFTypeRef>(CFSTR("NONE")));
}
static CFStringRef AttributeHandleFormat(CFTypeRef ah, CFDictionaryRef dict) {
transform_attribute *ta = ah2ta(ah);
return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), ta->transform->GetName(), ta->name);
}
static CFStringRef AttributeHandleDebugFormat(CFTypeRef ah) {
transform_attribute *ta = ah2ta(ah);
return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@ (%p)"), ta->transform->GetName(), ta->name, ta);
}
static void AttributeHandleFinalize(CFTypeRef ah)
{
transform_attribute *ta = ah2ta(ah);
if (!ta)
{
return;
}
if (ta->transform)
{
syslog(LOG_ERR, "over release of SecTransformAttributeRef at %p\n", ah);
abort();
}
if (ta->value)
{
CFRelease(ta->value);
}
if (ta->connections)
{
CFRelease(ta->connections);
}
if (ta->semaphore)
{
dispatch_release(ta->semaphore);
}
if (ta->attribute_changed_block)
{
Block_release(ta->attribute_changed_block);
}
if (ta->attribute_validate_block)
{
Block_release(ta->attribute_validate_block);
}
free(ta);
}
static CFHashCode ah_set_hash(const void *v) {
return CFHash(ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v)))->name);
}
static Boolean ah_set_equal(const void *v1, const void *v2) {
return CFEqual(ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v1)))->name, ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v2)))->name);
}
CFTypeID transform_attribute::cftype;
SecTransformAttributeRef Transform::makeAH(transform_attribute *ta) {
if (ta) {
SecTransformAttributeRef ah = _CFRuntimeCreateInstance(NULL, transform_attribute::cftype, sizeof(struct transform_attribute*), NULL);
if (!ah) {
return NULL;
}
*(struct transform_attribute **)(1 + (CFRuntimeBase*)ah) = ta;
return ah;
} else {
return NULL;
}
}
static pthread_key_t ah_search_key_slot;
static void destroy_ah_search_key(void *ah) {
CFRelease(ah);
pthread_setspecific(ah_search_key_slot, NULL);
}
SecTransformAttributeRef Transform::getAH(SecTransformStringOrAttributeRef attrib, bool create_ok, bool create_underscore_ok)
{
if (CFGetTypeID(attrib) == transform_attribute::cftype)
{
return (SecTransformAttributeRef)attrib;
}
CFStringRef label = (CFStringRef)attrib;
static dispatch_once_t once = 0;
const char *name = (const char *)"SecTransformAttributeRef";
static CFRuntimeClass ahclass;
static CFSetCallBacks tasetcb;
dispatch_once(&once, ^{
ahclass.className = name;
ahclass.copyFormattingDesc = AttributeHandleFormat;
ahclass.copyDebugDesc = AttributeHandleDebugFormat;
ahclass.finalize = AttributeHandleFinalize;
transform_attribute::cftype = _CFRuntimeRegisterClass(&ahclass);
if (transform_attribute::cftype == _kCFRuntimeNotATypeID) {
abort();
}
tasetcb.equal = ah_set_equal;
tasetcb.hash = ah_set_hash;
tasetcb.copyDescription = ah_set_describe;
pthread_key_create(&ah_search_key_slot, destroy_ah_search_key);
});
SecTransformAttributeRef search_for = pthread_getspecific(ah_search_key_slot);
if (!search_for)
{
search_for = makeAH((transform_attribute*)malloc(sizeof(transform_attribute)));
if (!search_for)
{
return NULL;
}
bzero(ah2ta(search_for), sizeof(transform_attribute));
pthread_setspecific(ah_search_key_slot, search_for);
}
if (!mAttributes)
{
mAttributes = CFSetCreateMutable(NULL, 0, &tasetcb);
}
ah2ta(search_for)->name = label;
SecTransformAttributeRef ah = static_cast<SecTransformAttributeRef>(const_cast<void*>(CFSetGetValue(mAttributes, search_for)));
if (ah == NULL && create_ok)
{
if (CFStringGetLength(label) && L'_' == CFStringGetCharacterAtIndex(label, 0) && !create_underscore_ok)
{
return NULL;
}
transform_attribute *ta = static_cast<transform_attribute *>(malloc(sizeof(transform_attribute)));
ah = makeAH(ta);
if (!ah)
{
return NULL;
}
ta->name = CFStringCreateCopy(NULL, label);
if (!ta->name)
{
free(ta);
return NULL;
}
CFIndex cnt = CFSetGetCount(mAttributes);
CFSetAddValue(mAttributes, ah);
if (CFSetGetCount(mAttributes) != cnt+1)
{
CFRelease(ta->name);
free(ta);
return NULL;
}
CFStringRef qname = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/%@"), dispatch_queue_get_label(this->mDispatchQueue), label);
CFIndex used, sz = 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(qname), kCFStringEncodingUTF8);
UInt8 *qnbuf = (UInt8 *)alloca(sz);
CFStringGetBytes(qname, CFRangeMake(0, CFStringGetLength(qname)), kCFStringEncodingUTF8, '?', FALSE, qnbuf, sz, &used);
qnbuf[used] = '\0';
ta->q = dispatch_queue_create((char*)qnbuf, NULL);
CFRelease(qname);
ta->semaphore = dispatch_semaphore_create(kMaxPendingTransactions);
ta->pushback_state = transform_attribute::pb_empty;
ta->pushback_value = NULL;
ta->value = NULL;
ta->connections = NULL;
ta->transform = this;
dispatch_set_target_queue(ta->q, mDispatchQueue);
ta->required = 0;
ta->requires_outbound_connection = 0;
ta->deferred = 0;
ta->stream = 0;
ta->ignore_while_externalizing = 0;
ta->has_incoming_connection = 0;
ta->direct_error_handling = 0;
ta->allow_external_sets = 0;
ta->has_been_deferred = 0;
ta->attribute_changed_block = NULL;
ta->attribute_validate_block = NULL;
}
return ah;
}
transform_attribute *Transform::getTA(SecTransformStringOrAttributeRef attrib, bool create_ok)
{
SecTransformAttributeRef ah = getAH(attrib, create_ok);
if (ah)
{
return ah2ta(ah);
}
else
{
return NULL;
}
}
void Transform::TAGetAll(transform_attribute **attributes) {
CFSetGetValues(mAttributes, (const void**)attributes);
CFIndex i, n = CFSetGetCount(mAttributes);
for(i = 0; i < n; ++i) {
attributes[i] = ah2ta(attributes[i]);
}
}
bool Transform::HasNoOutboundConnections()
{
CFIndex numAttributes = CFSetGetCount(mAttributes);
transform_attribute* attributes[numAttributes];
TAGetAll(attributes);
CFIndex i;
for (i = 0; i < numAttributes; ++i)
{
if (attributes[i]->connections && CFArrayGetCount(attributes[i]->connections) != 0)
{
return false;
}
}
return true;
}
bool Transform::HasNoInboundConnections()
{
CFIndex numAttributes = CFSetGetCount(mAttributes);
transform_attribute* attributes[numAttributes];
TAGetAll(attributes);
CFIndex i;
for (i = 0; i < numAttributes; ++i)
{
if (attributes[i]->has_incoming_connection)
{
return false;
}
}
return true;
}
CFIndex Transform::GetAttributeCount()
{
return CFSetGetCount(mAttributes);
}
Transform::Transform(CFStringRef transformType, CFStringRef CFobjectType) :
CoreFoundationObject(CFobjectType),
mIsActive(false),
mIsFinalizing(false),
mAlwaysSelfNotify(false),
mGroup(NULL),
mAbortError(NULL),
mTypeName(CFStringCreateCopy(NULL, transformType))
{
mAttributes = NULL;
mPushedback = NULL;
mProcessingPushbacks = FALSE;
if (internalID == _kCFRuntimeNotATypeID) {
(void)SecTransformNoData();
internalID = CoreFoundationObject::FindObjectType(gInternalCFObjectName);
}
char rname[10];
unsigned i;
for (i = 0; i < sizeof(rname) - 1; ++i)
{
rname[i] = RandomChar();
}
rname[i] = 0;
char *tname = const_cast<char*>(CFStringGetCStringPtr(transformType, kCFStringEncodingUTF8));
if (!tname) {
CFIndex sz = 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(transformType), kCFStringEncodingUTF8);
tname = static_cast<typeof(tname)>(alloca(sz));
if (tname) {
CFStringGetCString(transformType, tname, sz, kCFStringEncodingUTF8);
} else {
tname = const_cast<char*>("-");
}
}
char* name;
asprintf(&name, "%s-%s", rname, tname);
char *dqName;
asprintf(&dqName, "%s-%s", rname, tname);
char *aqName;
asprintf(&aqName, "aq-%s-%s", rname, tname);
mDispatchQueue = dispatch_queue_create(dqName, NULL);
dispatch_queue_set_specific(mDispatchQueue, &dispatchQueueToTransformKey, this, NULL);
mActivationQueue = dispatch_queue_create(aqName, NULL);
dispatch_set_target_queue(mActivationQueue, mDispatchQueue);
dispatch_suspend(mActivationQueue);
mActivationPending = dispatch_group_create();
AbortAH = getAH(kSecTransformAbortAttributeName, true);
transform_attribute *ta = ah2ta(AbortAH);
ta->ignore_while_externalizing = 1;
CFStringRef attributeName = CFStringCreateWithCStringNoCopy(NULL, name, 0, kCFAllocatorMalloc);
SetAttributeNoCallback(kSecTransformTransformName, attributeName);
CFRelease(attributeName);
free(dqName);
free(aqName);
DebugAH = getAH(kSecTransformDebugAttributeName, true);
ah2ta(DebugAH)->ignore_while_externalizing = 1;
ta = getTA(kSecTransformInputAttributeName, true);
ta->required = ta->deferred = ta->stream = 1;
ta->allow_external_sets = 0;
ta->value = NULL;
ta->has_been_deferred = 0;
ta = getTA(kSecTransformOutputAttributeName, true);
ta->requires_outbound_connection = ta->stream = 1;
}
static void run_and_release_finalizer(void *finalizer_block)
{
((dispatch_block_t)finalizer_block)();
Block_release(finalizer_block);
}
static void set_dispatch_finalizer(dispatch_object_t object, dispatch_block_t finalizer)
{
finalizer = Block_copy(finalizer);
dispatch_set_context(object, finalizer);
dispatch_set_finalizer_f(object, run_and_release_finalizer);
}
void Transform::FinalizePhase2()
{
delete this;
}
void Transform::FinalizeForClang()
{
CFIndex numAttributes = CFSetGetCount(mAttributes);
SecTransformAttributeRef handles[numAttributes];
CFSetGetValues(mAttributes, (const void**)&handles);
for(CFIndex i = 0; i < numAttributes; ++i) {
SecTransformAttributeRef ah = handles[i];
transform_attribute *ta = ah2ta(ah);
set_dispatch_finalizer(ta->q, ^{
ta->transform = NULL;
CFRelease(ah);
});
if (__sync_bool_compare_and_swap(&ta->pushback_state, transform_attribute::pb_value, transform_attribute::pb_discard)) {
dispatch_resume(ta->q);
}
dispatch_release(ta->q);
}
dispatch_group_notify(mActivationPending, mDispatchQueue, ^{
if (mActivationQueue != NULL) {
dispatch_resume(mActivationQueue);
dispatch_release(mActivationQueue);
}
set_dispatch_finalizer(mDispatchQueue, ^{
FinalizePhase2();
});
dispatch_release(mDispatchQueue);
});
}
void Transform::Finalize()
{
dispatch_block_t continue_finalization = ^{ this->FinalizeForClang(); };
dispatch_block_t mark_as_finalizing = ^{ this->mIsFinalizing = true; };
if (this == dispatch_get_specific(&dispatchQueueToTransformKey)) {
mark_as_finalizing();
} else {
dispatch_sync(mDispatchQueue, mark_as_finalizing);
}
if (mGroup) {
(void)transforms_assume(mGroup->mIsFinalizing); mGroup->AddAllChildrenFinalizedCallback(mDispatchQueue, continue_finalization);
mGroup->ChildStartedFinalization(this);
} else {
dispatch_async(mDispatchQueue, continue_finalization);
}
}
Transform::~Transform()
{
CFRelease(mAttributes);
if (mAbortError) {
CFRelease(mAbortError);
mAbortError = NULL;
}
mDispatchQueue = (dispatch_queue_t)0xdeadbeef;
CFRelease(mTypeName);
if (NULL != mPushedback)
{
CFRelease(mPushedback);
}
dispatch_release(mActivationPending);
}
CFStringRef Transform::GetName() {
return (CFStringRef)GetAttribute(kSecTransformTransformName);
}
CFTypeID Transform::GetCFTypeID()
{
return CoreFoundationObject::FindObjectType(CFSTR("SecTransform"));
}
std::string Transform::DebugDescription()
{
return CoreFoundationObject::DebugDescription() + "|SecTransform|" + StringFromCFString(this->GetName());
}
CFErrorRef Transform::SendMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type, CFTypeRef value)
{
SecTransformAttributeRef ah = getAH(key, true);
transform_attribute *ta = ah2ta(ah);
switch (type)
{
case kSecTransformMetaAttributeRequired:
ta->required = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0;
break;
case kSecTransformMetaAttributeRequiresOutboundConnection:
ta->requires_outbound_connection = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0;
break;
case kSecTransformMetaAttributeDeferred:
ta->deferred = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0;
break;
case kSecTransformMetaAttributeStream:
ta->stream = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0;
break;
case kSecTransformMetaAttributeHasOutboundConnections:
return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeHasOutboundConnections for %@ (or any other attribute)", ah);
case kSecTransformMetaAttributeHasInboundConnection:
return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeHasInboundConnection for %@ (or any other attribute)", ah);
case kSecTransformMetaAttributeCanCycle:
return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "kSecTransformMetaAttributeCanCycle not yet supported (%@)", ah);
case kSecTransformMetaAttributeExternalize:
ta->ignore_while_externalizing = CFBooleanGetValue((CFBooleanRef)value) ? 0 : 1;
break;
case kSecTransformMetaAttributeValue:
return SetAttributeNoCallback(ah, value);
case kSecTransformMetaAttributeRef:
return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeRef for %@ (or any other attribute)", ah);
case kSecTransformMetaAttributeName:
return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeName for %@ (or any other attribute)", ah);
default:
return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set unknown meta attribute #%d to %@ on %@", type, value, key);
}
return NULL;
}
CFTypeRef Transform::GetMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type) {
SecTransformAttributeRef ah = getAH(key, true);
transform_attribute *ta = ah2ta(ah);
switch (type) {
case kSecTransformMetaAttributeRequired:
return (CFTypeRef)(ta->required ? kCFBooleanTrue : kCFBooleanFalse);
case kSecTransformMetaAttributeRequiresOutboundConnection:
return (CFTypeRef)(ta->requires_outbound_connection ? kCFBooleanTrue : kCFBooleanFalse);
case kSecTransformMetaAttributeDeferred:
return (CFTypeRef)(ta->deferred ? kCFBooleanTrue : kCFBooleanFalse);
case kSecTransformMetaAttributeStream:
return (CFTypeRef)(ta->stream ? kCFBooleanTrue : kCFBooleanFalse);
case kSecTransformMetaAttributeHasOutboundConnections:
return (CFTypeRef)((ta->connections && CFArrayGetCount(ta->connections)) ? kCFBooleanTrue : kCFBooleanFalse);
case kSecTransformMetaAttributeHasInboundConnection:
return (CFTypeRef)(ta->has_incoming_connection ? kCFBooleanTrue : kCFBooleanFalse);
case kSecTransformMetaAttributeCanCycle:
return (CFTypeRef)kCFBooleanFalse;
case kSecTransformMetaAttributeExternalize:
return (CFTypeRef)(ta->ignore_while_externalizing ? kCFBooleanFalse : kCFBooleanTrue);
case kSecTransformMetaAttributeRef:
return ah;
case kSecTransformMetaAttributeValue:
return ta->value;
case kSecTransformMetaAttributeName:
return ta->name;
default:
return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't get unknown meta attribute #%d from %@", type, key);
break;
}
return NULL;
}
CFErrorRef Transform::RefactorErrorToIncludeAbortingTransform(CFErrorRef sourceError)
{
CFIndex code = CFErrorGetCode(sourceError);
CFStringRef domain = CFErrorGetDomain(sourceError);
CFDictionaryRef oldUserInfo = CFErrorCopyUserInfo(sourceError);
CFMutableDictionaryRef userInfo = CFDictionaryCreateMutableCopy(NULL, 0, oldUserInfo);
CFRelease(oldUserInfo);
CFDictionaryAddValue(userInfo, kSecTransformAbortOriginatorKey, GetCFObject());
CFErrorRef newError = CFErrorCreate(NULL, domain, code, userInfo);
CFRelease(userInfo);
return newError;
}
void Transform::AbortJustThisTransform(CFErrorRef abortErr)
{
(void)transforms_assume(abortErr);
(void)transforms_assume(dispatch_get_specific(&dispatchQueueToTransformKey) == this);
Boolean wasActive = mIsActive;
if (OSAtomicCompareAndSwapPtr(NULL, (void *)abortErr, (void**)&mAbortError)) {
CFRetain(abortErr);
dispatch_block_t abortAction = ^{
if (!wasActive) {
this->AbortAllTransforms(abortErr);
}
SecTransformAttributeRef GCC_BUG_WORKAROUND inputAttributeHandle = getAH(kSecTransformInputAttributeName, false);
AttributeChanged(inputAttributeHandle, abortErr);
try_pushbacks();
};
if (mIsActive) {
abortAction();
} else {
dispatch_async(mActivationQueue, abortAction);
}
} else {
Debug("%@ set to %@ while processing ABORT=%@, this extra set will be ignored", AbortAH, abortErr, mAbortError);
}
}
void Transform::AbortAllTransforms(CFTypeRef err)
{
Debug("%@ set to %@, aborting\n", AbortAH, err);
CFErrorRef error = NULL;
CFTypeRef replacementErr = NULL;
if (CFGetTypeID(err) != CFErrorGetTypeID())
{
replacementErr = err = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "ABORT set to a %@ (%@) not a %@", CFCopyTypeIDDescription(CFGetTypeID(err)), err, CFCopyTypeIDDescription(CFErrorGetTypeID()));
}
error = RefactorErrorToIncludeAbortingTransform((CFErrorRef)err);
if (replacementErr)
{
CFRelease(replacementErr);
}
GroupTransform *root = GetRootGroup();
if (root)
{
dispatch_group_t all_aborted = dispatch_group_create();
root->ForAllNodesAsync(false, all_aborted, ^(Transform* t){
t->AbortJustThisTransform(error);
});
dispatch_group_notify(all_aborted, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) {
CFRelease(error);
dispatch_release(all_aborted);
});
}
else
{
(void)transforms_assume_zero(mIsActive);
this->AbortJustThisTransform(error);
}
}
CFErrorRef Transform::Disconnect(Transform* destinationTransform, CFStringRef myKey, CFStringRef hisKey)
{
CFIndex i;
transform_attribute *src = getTA(myKey, true);
SecTransformAttributeRef dst = destinationTransform->getAH(hisKey);
if (src->connections == NULL)
{
return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Cannot find transform in destination.");
}
CFIndex numConnections = CFArrayGetCount(src->connections);
for (i = 0; i < numConnections; ++i)
{
if (CFArrayGetValueAtIndex(src->connections, i) == dst)
{
CFArrayRemoveValueAtIndex(src->connections, i);
numConnections = CFArrayGetCount(src->connections);
}
transform_attribute* dstTA = ah2ta(dst);
dstTA->has_incoming_connection = false;
}
if (HasNoInboundConnections() && HasNoOutboundConnections())
{
mGroup->RemoveMemberFromGroup(GetCFObject());
mGroup = NULL;
}
return NULL;
}
CFErrorRef Transform::Connect(GroupTransform *group, Transform* destinationTransform, CFStringRef destAttr, CFStringRef srcAttr)
{
if (group == NULL)
{
CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make connections without a specific group (do not call with group = NULL)");
return err;
}
GroupTransform *newSourceGroup = mGroup;
GroupTransform *newDestinationGroup = destinationTransform->mGroup;
if (mGroup == NULL || mGroup == this)
{
newSourceGroup = group;
}
if (destinationTransform->mGroup == NULL || destinationTransform->mGroup == destinationTransform)
{
newDestinationGroup = group;
}
if (newSourceGroup != newDestinationGroup && mGroup)
{
CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make connections between transforms in different groups (%@ is in %@, %@ is in %@)", GetName(), newSourceGroup->GetName(), destinationTransform->GetName(), newDestinationGroup->GetName());
return err;
}
if (!validConnectionPoint(srcAttr)) {
CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make a connection from non-exported attribute %@ of %@", srcAttr, this->GetName());
return err;
}
if (!destinationTransform->validConnectionPoint(destAttr)) {
CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make a connection to non-exported attribute %@ of %@", destAttr, destinationTransform->GetName());
return err;
}
mGroup = newSourceGroup;
destinationTransform->mGroup = newDestinationGroup;
group->AddMemberToGroup(this->GetCFObject());
group->AddMemberToGroup(destinationTransform->GetCFObject());
transform_attribute *src = this->getTA(srcAttr, true);
SecTransformAttributeRef dst = destinationTransform->getAH(destAttr);
if (!src->connections)
{
src->connections = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(src->connections, dst);
ah2ta(dst)->has_incoming_connection = 1;
return NULL;
}
bool Transform::validConnectionPoint(CFStringRef attributeName)
{
return true;
}
CFErrorRef Transform::SetAttributeNoCallback(SecTransformStringOrAttributeRef key, CFTypeRef value)
{
SecTransformAttributeRef ah = getAH(key, true);
if (!ah)
{
abort();
}
transform_attribute *ta = ah2ta(ah);
if (ah == AbortAH && value && (mIsActive || !ta->deferred))
{
AbortAllTransforms(value);
return CreateSecTransformErrorRef(kSecTransformErrorAbortInProgress, "Abort started");
}
bool do_propagate = true;
if (!ta->has_been_deferred)
{
bool doNotRetain = false;
if (value)
{
CFStringRef name = ta->name;
if (CFGetTypeID(value) == CFReadStreamGetTypeID())
{
CFTypeRef src = StreamSource::Make((CFReadStreamRef) value, this, name);
value = src;
do_propagate = false;
ta->has_been_deferred = 1;
doNotRetain = true;
}
else if (ta->deferred && !mIsActive)
{
if (ta->deferred)
{
Debug("%@ deferred value=%p\n", ah, value);
}
CFTypeRef src = SingleShotSource::Make(value, this, name);
ta->has_been_deferred = 1;
value = src;
do_propagate = false;
doNotRetain = true;
}
else
{
ta->has_been_deferred = 0;
}
}
if (ta->value != value) {
if (value && !doNotRetain) {
CFRetain(value);
}
if (ta->value) {
CFRelease(ta->value);
}
}
ta->value = value;
}
if (ta->connections && mIsActive && do_propagate && !(mAbortError || mIsFinalizing))
{
Debug("Propagating from %@ to %@\n", ah, ta->connections);
CFIndex i, numConnections = CFArrayGetCount(ta->connections);
for(i = 0; i < numConnections; ++i) {
SecTransformAttributeRef ah = static_cast<SecTransformAttributeRef>(const_cast<void *>(CFArrayGetValueAtIndex(ta->connections, i)));
Transform *tt = ah2ta(ah)->transform;
if (NULL != tt)
{
if (tt->mIsActive)
{
tt->SetAttribute(ah, value);
}
else
{
dispatch_block_t setAttribute = ^{
tt->SetAttribute(ah, value);
};
dispatch_async(ah2ta(ah)->q, ^(void) {
if (tt->mIsActive) {
setAttribute();
} else {
dispatch_async(ah2ta(ah)->transform->mActivationQueue, setAttribute);
}
});
}
}
}
}
return NULL;
}
CFErrorRef Transform::ExternalSetAttribute(CFTypeRef key, CFTypeRef value)
{
if (!mIsActive)
{
return this->SetAttribute(key, value);
}
else
{
SecTransformAttributeRef ah = getAH(key, false);
if (ah != NULL && ah2ta(ah)->allow_external_sets)
{
return this->SetAttribute(static_cast<CFTypeRef>(ah), value);
}
else
{
return CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "%@ can not be set while %@ is executing", ah, this->GetName());
}
}
}
CFErrorRef Transform::SetAttribute(CFTypeRef key, CFTypeRef value)
{
if (mAbortError)
{
return CreateSecTransformErrorRef(kSecTransformErrorAborted, "ABORT has been sent to the transform (%@)", mAbortError);
}
SecTransformAttributeRef ah;
if (CFGetTypeID(key) == transform_attribute::cftype)
{
ah = key;
}
else if (CFGetTypeID(key) == CFStringGetTypeID())
{
ah = getAH(static_cast<CFStringRef>(key));
if (!ah)
{
return CreateSecTransformErrorRef(kSecTransformErrorUnsupportedAttribute, "Can't set attribute %@ in transform %@", key, GetName());
}
}
else
{
return CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "Transform::SetAttribute called with %@, requires a string or an AttributeHandle", key);
}
if (value != NULL)
{
CFRetain(value); }
transform_attribute *ta = ah2ta(ah);
dispatch_block_t set = ^{
Do(ah, value);
dispatch_semaphore_signal(ta->semaphore);
if (value != NULL)
{
CFRelease(value);
}
};
if (mIsActive)
{
dispatch_async(ta->q, set);
}
else
{
dispatch_sync(ta->q, set);
}
if (dispatch_semaphore_wait(ta->semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC))) {
Debug("Send from %@ to %@ is still waiting\n", GetName(), ah);
dispatch_semaphore_wait(ta->semaphore, DISPATCH_TIME_FOREVER);
}
return mAbortError;
}
CFErrorRef Transform::SendAttribute(SecTransformStringOrAttributeRef key, CFTypeRef value)
{
return SetAttributeNoCallback(key, value);
}
CFTypeRef Transform::GetAttribute(SecTransformStringOrAttributeRef key)
{
struct transform_attribute *ta = getTA(key, false);
if (ta == NULL || ta->value == NULL) {
return NULL;
}
if (CFGetTypeID(ta->value) == internalID)
{
Source* source = (Source*) CoreFoundationHolder::ObjectFromCFType(ta->value);
return source->GetValue();
}
else
{
return ta->value;
}
}
CFErrorRef Transform::Pushback(SecTransformAttributeRef ah, CFTypeRef value)
{
CFErrorRef result = NULL;
transform_attribute *ta = ah2ta(ah);
if (!(ta->pushback_state == transform_attribute::pb_empty || ta->pushback_state == transform_attribute::pb_repush))
{
CFErrorRef error = fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidOperation, CFSTR("Can not pushback new value until old value has been processed"));
SetAttribute(kSecTransformAbortAttributeName, error);
return error;
}
if (value == NULL && ta->pushback_value == NULL && ta->pushback_state == transform_attribute::pb_repush)
{
ta->pushback_state = transform_attribute::pb_presented_once;
} else
{
ta->pushback_state = transform_attribute::pb_value;
}
if (value)
{
CFRetain(value);
}
ta->pushback_value = value;
dispatch_suspend(ta->q);
if (!mPushedback)
{
mPushedback = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(mPushedback, ah);
return result;
}
void Transform::try_pushbacks() {
if (!mPushedback || !CFArrayGetCount(mPushedback)) {
mProcessingPushbacks = FALSE;
return;
}
CFArrayRef pb = (CFArrayRef)mPushedback;
mPushedback = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFIndex i, n = CFArrayGetCount(pb);
int succeeded = 0;
for(i = 0; i < n; ++i)
{
SecTransformAttributeRef ah = CFArrayGetValueAtIndex(pb, i);
transform_attribute *ta = ah2ta(ah);
ta->pushback_state = transform_attribute::pb_repush;
CFTypeRef v = ta->pushback_value;
ta->pushback_value = NULL;
Do(ah, v);
if (v)
{
CFRelease(v);
}
if (ta->pushback_state == transform_attribute::pb_repush) {
ta->pushback_state = transform_attribute::pb_empty;
succeeded++;
}
dispatch_resume(ta->q);
}
CFRelease(pb);
if (succeeded && CFArrayGetCount(mPushedback)) {
dispatch_async(mDispatchQueue, ^{ try_pushbacks(); });
} else {
mProcessingPushbacks = FALSE;
}
}
void Transform::Debug(const char *cfmt, ...) {
CFTypeRef d = ah2ta(DebugAH)->value;
if (d) {
CFWriteStreamRef out = NULL;
if (CFGetTypeID(d) == CFWriteStreamGetTypeID()) {
out = (CFWriteStreamRef)d;
} else {
static dispatch_once_t once;
static CFWriteStreamRef StdErrWriteStream;
dispatch_once(&once, ^{
auto GCC_BUG_WORKAROUND CFURLRef GCC_BUG_WORKAROUND p = CFURLCreateWithFileSystemPath(NULL, CFSTR("/dev/stderr"), kCFURLPOSIXPathStyle, FALSE);
StdErrWriteStream = CFWriteStreamCreateWithFile(NULL, p);
CFWriteStreamOpen(StdErrWriteStream);
CFRelease(p);
});
out = StdErrWriteStream;
}
va_list ap;
va_start(ap, cfmt);
CFStringRef fmt = CFStringCreateWithCString(NULL, cfmt, kCFStringEncodingUTF8);
CFStringRef str = CFStringCreateWithFormatAndArguments(NULL, NULL, fmt, ap);
CFRelease(fmt);
va_end(ap);
CFIndex sz = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8);
sz += 1;
CFIndex used = 0;
unsigned char *buf;
bool needs_free = true;
buf = (unsigned char*)malloc(sz);
if (buf) {
CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, '?', FALSE, buf, sz, &used);
} else {
buf = (unsigned char *)"malloc failure during Transform::Debug\n";
needs_free = false;
}
static dispatch_once_t once;
static dispatch_queue_t print_q;
dispatch_once(&once, ^{
print_q = dispatch_queue_create("com.apple.security.debug.print_queue", 0);
dispatch_set_target_queue((dispatch_object_t)print_q, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
});
dispatch_async(print_q, ^{
CFWriteStreamWrite(out, buf, used);
if (needs_free) {
free(buf);
}
});
CFRelease(str);
}
}
void Transform::Do(SecTransformAttributeRef ah, CFTypeRef value)
{
transform_attribute *ta = ah2ta(ah);
if (ta->pushback_state == transform_attribute::pb_discard)
{
return;
}
(void)transforms_assume(dispatch_get_current_queue() == ((ta->pushback_state == transform_attribute::pb_repush) ? mDispatchQueue : ta->q));
if (mIsFinalizing)
{
Debug("Ignoring value %p sent to %@ (on queue %s) during finalization", value, ah, dispatch_queue_get_label(dispatch_get_current_queue()));
return;
}
SetAttributeNoCallback(ah, value);
if (mAbortError && (!(ah == this->AbortAH || ah == getTA(CFSTR("INPUT"), true)) && (value == NULL || CFGetTypeID(value) != CFErrorGetTypeID())))
{
if (value) {
Debug("Ignoring value (%@) sent to %@ during abort\n", value, ah);
} else {
Debug("Ignoring NULL sent to %@ during abort\n", ah);
}
return;
}
if (mIsActive || (mAlwaysSelfNotify && !ta->deferred))
{
Debug("AttributeChanged: %@ (%s) = %@\n", ah, mIsActive ? "is executing" : "self notify set", value ? value : (CFTypeRef)CFSTR("(NULL)"));
AttributeChanged(ah, value);
}
if (mPushedback && CFArrayGetCount(mPushedback) && !mProcessingPushbacks)
{
Debug("will process pushbacks (%@) later\n", mPushedback);
mProcessingPushbacks = TRUE;
dispatch_async(mDispatchQueue, ^{ try_pushbacks(); });
}
return;
}
void Transform::AttributeChanged(CFStringRef name, CFTypeRef value)
{
}
void Transform::AttributeChanged(SecTransformAttributeRef ah, CFTypeRef value)
{
AttributeChanged(ah2ta(ah)->name, value);
}
CFArrayRef Transform::GetAllAH() {
CFIndex cnt = CFSetGetCount(mAttributes);
const void **values = (const void **)alloca(sizeof(void*)*cnt);
CFSetGetValues(mAttributes, values);
return CFArrayCreate(NULL, values, cnt, &kCFTypeArrayCallBacks);
}
CFTypeRef Transform::Execute(dispatch_queue_t deliveryQueue, SecMessageBlock deliveryBlock, CFErrorRef* errorRef)
{
if (!mGroup)
{
CFTypeRef g = GroupTransform::Make();
mGroup = (GroupTransform*)CoreFoundationHolder::ObjectFromCFType(g);
mGroup->AddMemberToGroup(this->GetCFObject());
SecMessageBlock smb = ^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
{
deliveryBlock(message, error, isFinal);
if (isFinal)
{
dispatch_async(this->mDispatchQueue, ^{
CFRelease(g);
});
}
};
CFTypeRef ret = this->Execute(deliveryQueue, deliveryBlock ? smb : (SecMessageBlock) NULL, errorRef);
if (!deliveryBlock)
{
CFRelease(g);
}
return ret;
}
if (mIsActive)
{
if (errorRef)
{
*errorRef = CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform has already executed, it may not be executed again.", GetName());
}
return NULL;
}
GroupTransform *rootGroup = GetRootGroup();
CFRetain(rootGroup->GetCFObject());
CFTypeRef result = NULL;
CFTypeRef monitorRef = BlockMonitor::Make(deliveryQueue, deliveryBlock);
__block CFStringRef outputAttached = NULL;
dispatch_queue_t p2 = dispatch_queue_create("activate phase2", NULL);
dispatch_queue_t p3 = dispatch_queue_create("activate phase3", NULL);
dispatch_suspend(p2);
dispatch_suspend(p3);
CFErrorRef temp = TraverseTransform(NULL, ^(Transform *t){
return t->ExecuteOperation(outputAttached, (SecMonitorRef)monitorRef, p2, p3);
});
rootGroup->mIsActive = true;
rootGroup->StartingExecutionInGroup();
dispatch_resume(p2);
dispatch_sync(p2, ^{ dispatch_resume(p3); });
dispatch_sync(p3, ^{ dispatch_release(p2); });
dispatch_release(p3);
if (errorRef)
{
*errorRef = temp;
}
if (temp) {
CFRelease(rootGroup->GetCFObject());
CFRelease(monitorRef);
rootGroup->StartedExecutionInGroup(false);
return NULL;
}
dispatch_group_t initialized = dispatch_group_create();
rootGroup->ForAllNodesAsync(true, initialized, ^(Transform*t) {
t->Initialize();
});
dispatch_group_notify(initialized, rootGroup->mDispatchQueue, ^{
dispatch_release(initialized);
dispatch_group_t activated = dispatch_group_create();
dispatch_group_enter(activated);
dispatch_async(rootGroup->mDispatchQueue, ^{
rootGroup->ForAllNodesAsync(true, activated, ^(Transform*t) {
t->ActivateInputs();
});
dispatch_group_leave(activated);
});
dispatch_group_notify(activated, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_release(activated);
CFRelease(monitorRef);
rootGroup->StartedExecutionInGroup(true);
});
});
return result;
}
void Transform::Initialize()
{
}
static void ActivateInputs_set(const void *v, void *unused) {
transform_attribute *ta = static_cast<transform_attribute *>(ah2ta(const_cast<void *>(v)));
if (ta->value && internalID == CFGetTypeID(ta->value)) {
Source* s = (Source*) CoreFoundationHolder::ObjectFromCFType(ta->value);
s->Activate();
}
}
void Transform::ActivateInputs()
{
(void)transforms_assume_zero(mIsActive && this != dispatch_get_specific(&dispatchQueueToTransformKey));
if (!mIsFinalizing) {
CFSetApplyFunction(mAttributes, ActivateInputs_set, NULL);
}
}
CFErrorRef Transform::ForAllNodes(bool parallel, bool includeOwningGroup, Transform::TransformOperation op)
{
GroupTransform *g = GetRootGroup();
if (g) {
return g->ForAllNodes(parallel, includeOwningGroup, op);
} else {
return op(this);
}
}
CFErrorRef Transform::TraverseTransform(CFMutableSetRef visited, TransformOperation t)
{
return ForAllNodes(true, true, t);
}
CFErrorRef Transform::ExecuteOperation(CFStringRef &outputAttached, SecMonitorRef output, dispatch_queue_t phase2, dispatch_queue_t phase3)
{
if (!mGroup) {
return NULL;
}
if (!TransformCanExecute())
{
return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly, "The transform %@ was not ready for execution.", GetName());
}
CFIndex i, numAttributes = CFSetGetCount(mAttributes);
transform_attribute **attributes = (transform_attribute **)alloca(numAttributes * sizeof(transform_attribute *));
TAGetAll(attributes);
CFMutableArrayRef still_need = NULL;
for(i = 0; i < numAttributes; ++i) {
transform_attribute *ta = attributes[i];
if (ta->required && ta->value == NULL && !ta->has_incoming_connection) {
if (!still_need) {
still_need = CFArrayCreateMutable(NULL, i, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(still_need, ta->name);
}
}
if (still_need) {
CFStringRef elist = CFStringCreateByCombiningStrings(NULL, still_need, CFSTR(", "));
CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorMissingParameter, "Can not execute %@, missing required attributes: %@", GetName(), elist);
CFRelease(elist);
CFRelease(still_need);
return err;
}
numAttributes = CFSetGetCount(mAttributes);
attributes = (transform_attribute **)alloca(numAttributes * sizeof(transform_attribute *));
TAGetAll(attributes);
for (i = 0; i < numAttributes; ++i)
{
transform_attribute *ta = attributes[i];
CFIndex arraySize = ta->connections ? CFArrayGetCount(ta->connections) : 0;
if (arraySize == 0 && ta->requires_outbound_connection)
{
if (CFStringCompare(ta->name, kSecTransformOutputAttributeName, 0) == kCFCompareEqualTo) {
if (outputAttached)
{
return CreateSecTransformErrorRef(kSecTransformErrorMoreThanOneOutput, "Both %@ and %@ have loose outputs, attach one to something", outputAttached, ta->transform->GetName());
}
dispatch_async(phase2, ^{
SecTransformConnectTransformsInternal(mGroup->GetCFObject(),
GetCFObject(), kSecTransformOutputAttributeName,
output, kSecTransformInputAttributeName);
});
outputAttached = ta->transform->GetName();
Monitor* m = (Monitor*) CoreFoundationHolder::ObjectFromCFType(output);
m->mIsActive = true;
} else {
return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly, "Attribute %@ (in %@) requires an outbound connection and doesn't have one", ta->name, GetName());
}
break;
}
}
dispatch_async(phase3, ^{
phase3Activation();
});
return NULL;
}
void Transform::DoPhase3Activation()
{
this->mIsActive = true;
CFErrorRef initError = TransformStartingExecution();
if (initError)
{
this->SendAttribute(AbortAH, initError);
}
dispatch_resume(this->mActivationQueue);
dispatch_group_async(this->mActivationPending, this->mActivationQueue, ^{
dispatch_release(this->mActivationQueue);
this->mActivationQueue = NULL;
});
}
void Transform::phase3Activation()
{
dispatch_async(this->mDispatchQueue, ^
{
DoPhase3Activation();
});
}
Boolean Transform::TransformCanExecute()
{
return true;
}
CFErrorRef Transform::TransformStartingExecution()
{
return NULL;
}
bool Transform::IsExternalizable()
{
return true;
}
static const void *CFTypeOrNULLRetain(CFAllocatorRef allocator, const void *value) {
if (value != NULL) {
return CFRetain(value);
} else {
return value;
}
}
static void CFTypeOrNULLRelease(CFAllocatorRef allocator, const void *value) {
if (value != NULL) {
CFRelease(value);
}
}
static CFStringRef CFTypeOrNULLCopyDescription (const void *value) {
if (value != NULL) {
return CFCopyDescription(value);
} else {
return CFSTR("NULL");
}
}
static Boolean CFTypeOrNULLEqual(const void *value1, const void *value2) {
if (value1 == NULL && value2 == NULL) {
return TRUE;
} else {
if (value1 == NULL || value2 == NULL) {
return FALSE;
} else {
return CFEqual(value1, value2);
}
}
}
CFDictionaryRef Transform::GetAHDictForSaveState(SecTransformStringOrAttributeRef key)
{
SecTransformMetaAttributeType types[] =
{
kSecTransformMetaAttributeRequired,
kSecTransformMetaAttributeRequiresOutboundConnection,
kSecTransformMetaAttributeDeferred,
kSecTransformMetaAttributeStream,
kSecTransformMetaAttributeCanCycle,
kSecTransformMetaAttributeValue
};
CFIndex i, cnt = sizeof(types)/sizeof(SecTransformMetaAttributeType);
CFTypeRef values[cnt];
CFNumberRef keys[cnt];
key = getAH(key);
for(i = 0; i < cnt; ++i)
{
values[i] = GetMetaAttribute(key, types[i]);
int tmp = (int)types[i];
keys[i] = CFNumberCreate(NULL, kCFNumberIntType, &tmp);
}
static CFDictionaryValueCallBacks CFTypeOrNULL;
static dispatch_once_t once;
dispatch_block_t b =
^{
CFTypeOrNULL.version = 0;
CFTypeOrNULL.retain = CFTypeOrNULLRetain;
CFTypeOrNULL.release = CFTypeOrNULLRelease;
CFTypeOrNULL.copyDescription = CFTypeOrNULLCopyDescription;
CFTypeOrNULL.equal = CFTypeOrNULLEqual;
};
dispatch_once(&once, b);
CFDictionaryRef ret = CFDictionaryCreate(NULL, (const void**)&keys, (const void**)&values, cnt, &kCFTypeDictionaryKeyCallBacks, &CFTypeOrNULL);
for(i = 0; i < cnt; ++i)
{
CFRelease(keys[i]);
}
return ret;
}
CFDictionaryRef Transform::CopyState()
{
CFIndex i, j, cnt = CFSetGetCount(mAttributes);
transform_attribute *attrs[cnt];
CFStringRef names[cnt];
CFDictionaryRef values[cnt];
TAGetAll(attrs);
for(i = j = 0; i < cnt; ++i)
{
transform_attribute *ta = attrs[i];
if (!ta->ignore_while_externalizing)
{
names[j] = ta->name;
values[j++] = GetAHDictForSaveState(ta->name);
}
}
CFDictionaryRef result = CFDictionaryCreate(NULL, (const void**)&names, (const void**)&values, j, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
for(i = j = 0; i < cnt; ++i)
{
transform_attribute *ta = attrs[i];
if (!ta->ignore_while_externalizing)
{
CFRelease(values[j++]);
}
}
return result;
}
void Transform::RestoreState(CFDictionaryRef state)
{
CFIndex i, cnt = CFDictionaryGetCount(state);
const void
**keys = (const void **)alloca(sizeof(void*)*cnt),
**values = (const void **)alloca(sizeof(void*)*cnt);
CFDictionaryGetKeysAndValues(state, keys, values);
for(i = 0; i < cnt; i++)
{
SecTransformAttributeRef ah = getAH(keys[i]);
if (NULL == ah)
{
continue;
}
CFIndex j, meta_cnt = CFDictionaryGetCount((CFDictionaryRef)values[i]);
const void **types = (const void**)alloca(sizeof(void*)*meta_cnt), **meta_values = (const void**)alloca(sizeof(void*)*meta_cnt);
CFDictionaryGetKeysAndValues((CFDictionaryRef)values[i], types, meta_values);
int t;
for(j = 0; j < meta_cnt; ++j)
{
CFNumberGetValue((CFNumberRef)types[j], kCFNumberIntType, &t);
if (t == kSecTransformMetaAttributeValue)
{
if (meta_values[j]) {
SetAttribute(ah, meta_values[j]);
}
}
else
{
CFErrorRef result = SendMetaAttribute(ah, (SecTransformMetaAttributeType)t, meta_values[j]);
if (result)
{
CFRelease(result); }
}
}
CFErrorRef result = SendMetaAttribute(ah, kSecTransformMetaAttributeExternalize, kCFBooleanTrue);
if (result)
{
CFRelease(result); }
}
}
GroupTransform* Transform::GetRootGroup()
{
GroupTransform *g = mGroup;
if (g) {
while (g->mGroup) {
g = g->mGroup;
}
} else {
if (CFGetTypeID(this->GetCFObject()) == SecGroupTransformGetTypeID()) {
return (GroupTransform *)this;
}
}
return g;
}
CFDictionaryRef Transform::GetCustomExternalData()
{
return NULL;
}
void Transform::SetCustomExternalData(CFDictionaryRef customData)
{
return;
}
CFDictionaryRef Transform::Externalize(CFErrorRef* error)
{
if (mIsActive)
{
return (CFDictionaryRef)CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform is executing, you need to externalize it prior to execution", GetName());
}
__block CFMutableArrayRef transforms = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
__block CFMutableArrayRef connections = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
GroupTransform *root = GetRootGroup();
CFErrorRef err = ForAllNodes(false, true, ^(Transform *t) {
if (t != root) {
return t->ProcessExternalize(transforms, connections);
}
return (CFErrorRef)NULL;
});
if (NULL != err)
{
if (NULL != error)
{
*error = err;
}
return NULL;
}
CFMutableDictionaryRef output = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(output, EXTERN_TRANSFORM_TRANSFORM_ARRAY, transforms);
CFDictionaryAddValue(output, EXTERN_TRANSFORM_CONNECTION_ARRAY, connections);
CFRelease(connections);
CFRelease(transforms);
return output;
}
CFErrorRef Transform::ProcessExternalize(CFMutableArrayRef transforms, CFMutableArrayRef connections)
{
if (!IsExternalizable()) {
return NULL;
}
CFDictionaryRef state = CopyState();
if (state && CFGetTypeID(state) == CFErrorGetTypeID()) {
return (CFErrorRef)state;
}
CFMutableDictionaryRef node = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(node, EXTERN_TRANSFORM_NAME, GetName());
CFTypeRef type = CFStringCreateCopy(NULL, mTypeName);
CFDictionaryAddValue(node, EXTERN_TRANSFORM_TYPE, type);
CFRelease(type);
if (state != NULL)
{
CFDictionaryAddValue(node, EXTERN_TRANSFORM_STATE, state);
CFRelease(state);
}
CFDictionaryRef customItems = GetCustomExternalData();
if (NULL != customItems)
{
CFDictionaryAddValue(node, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY, customItems);
CFRelease(customItems);
}
CFArrayAppendValue(transforms, node);
CFRelease(node);
CFIndex numAttributes = CFSetGetCount(mAttributes);
transform_attribute *attributes[numAttributes];
TAGetAll(attributes);
CFIndex i;
for (i = 0; i < numAttributes; ++i)
{
CFIndex arraySize = attributes[i]->connections ? CFArrayGetCount(attributes[i]->connections) : 0;
if (arraySize != 0)
{
CFIndex j;
for (j = 0; j < arraySize; ++j)
{
transform_attribute *ta = ah2ta((SecTransformAttributeRef)CFArrayGetValueAtIndex(attributes[i]->connections, j));
if (!ta->transform->IsExternalizable()) {
continue;
}
CFMutableDictionaryRef connection =
CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(connection, EXTERN_TRANSFORM_FROM_NAME, GetName());
CFDictionaryAddValue(connection, EXTERN_TRANSFORM_FROM_ATTRIBUTE, attributes[i]->name);
CFDictionaryAddValue(connection, EXTERN_TRANSFORM_TO_NAME, ta->transform->GetName());
CFDictionaryAddValue(connection, EXTERN_TRANSFORM_TO_ATTRIBUTE, ta->name);
CFArrayAppendValue(connections, connection);
CFRelease(connection);
}
}
}
return NULL;
}