1#include <CoreServices/CoreServices.h>
2#include <Block.h>
3#include <libkern/OSAtomic.h>
4#include <syslog.h>
5#include "Transform.h"
6#include "StreamSource.h"
7#include "SingleShotSource.h"
8#include "Monitor.h"
9#include "Utilities.h"
10#include "c++utils.h"
11#include "misc.h"
12#include "SecTransformInternal.h"
13#include "GroupTransform.h"
14#include "GroupTransform.h"
15#include <pthread.h>
16
17static const int kMaxPendingTransactions = 20;
18
19static CFTypeID internalID = _kCFRuntimeNotATypeID;
20
21// Use &dispatchQueueToTransformKey as a key to dispatch_get_specific to map from
22// a transforms master, activation, or any attribute queue to the Transform*
23static unsigned char dispatchQueueToTransformKey;
24
25static char RandomChar()
26{
27	return arc4random() % 26 + 'A'; // good enough
28}
29
30
31static CFStringRef ah_set_describe(const void *v) {
32	transform_attribute *ta = ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v)));
33	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")));
34}
35
36static CFStringRef AttributeHandleFormat(CFTypeRef ah, CFDictionaryRef dict) {
37	transform_attribute *ta = ah2ta(ah);
38	return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), ta->transform->GetName(), ta->name);
39}
40
41static CFStringRef AttributeHandleDebugFormat(CFTypeRef ah) {
42	transform_attribute *ta = ah2ta(ah);
43	return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@ (%p)"), ta->transform->GetName(), ta->name, ta);
44}
45
46static void AttributeHandleFinalize(CFTypeRef ah)
47{
48	transform_attribute *ta = ah2ta(ah);
49	if (!ta)
50	{
51		return;
52	}
53
54	if (ta->transform)
55	{
56		// When we release AH's we clear out the transform pointer, so if we get here with transform!=NULL somebody
57		// has released an AH we are still using and we will crash very very soon (in our code) if we don't abort here
58		syslog(LOG_ERR, "over release of SecTransformAttributeRef at %p\n", ah);
59		abort();
60	}
61
62	if (ta->value)
63	{
64		CFRelease(ta->value);
65	}
66
67	// ta->q already released
68
69	if (ta->connections)
70	{
71		CFRelease(ta->connections);
72	}
73
74	if (ta->semaphore)
75	{
76		dispatch_release(ta->semaphore);
77	}
78
79	if (ta->attribute_changed_block)
80	{
81		Block_release(ta->attribute_changed_block);
82	}
83
84	if (ta->attribute_validate_block)
85	{
86		Block_release(ta->attribute_validate_block);
87	}
88
89	free(ta);
90}
91
92
93
94static CFHashCode ah_set_hash(const void *v) {
95	return CFHash(ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v)))->name);
96}
97
98static Boolean ah_set_equal(const void *v1, const void *v2) {
99	return CFEqual(ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v1)))->name, ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v2)))->name);
100}
101
102CFTypeID transform_attribute::cftype;
103
104SecTransformAttributeRef Transform::makeAH(transform_attribute *ta) {
105	if (ta) {
106		SecTransformAttributeRef ah = _CFRuntimeCreateInstance(NULL, transform_attribute::cftype, sizeof(struct transform_attribute*), NULL);
107		if (!ah) {
108			return NULL;
109		}
110		*(struct transform_attribute **)(1 + (CFRuntimeBase*)ah) = ta;
111		return ah;
112	} else {
113		return NULL;
114	}
115}
116
117static pthread_key_t ah_search_key_slot;
118
119static void destroy_ah_search_key(void *ah) {
120	CFRelease(ah);
121	pthread_setspecific(ah_search_key_slot, NULL);
122}
123
124
125
126SecTransformAttributeRef Transform::getAH(SecTransformStringOrAttributeRef attrib, bool create_ok, bool create_underscore_ok)
127{
128	if (CFGetTypeID(attrib) == transform_attribute::cftype)
129	{
130		return (SecTransformAttributeRef)attrib;
131	}
132
133	CFStringRef label = (CFStringRef)attrib;
134	static dispatch_once_t once = 0;
135	const char *name = (const char *)"SecTransformAttributeRef";
136	static CFRuntimeClass ahclass;
137	static CFSetCallBacks tasetcb;
138
139	dispatch_once(&once, ^{
140		ahclass.className = name;
141		ahclass.copyFormattingDesc = AttributeHandleFormat;
142		ahclass.copyDebugDesc = AttributeHandleDebugFormat;
143		ahclass.finalize = AttributeHandleFinalize;
144		transform_attribute::cftype = _CFRuntimeRegisterClass(&ahclass);
145		if (transform_attribute::cftype == _kCFRuntimeNotATypeID) {
146			abort();
147		}
148
149		tasetcb.equal = ah_set_equal;
150		tasetcb.hash = ah_set_hash;
151		tasetcb.copyDescription = ah_set_describe;
152
153		pthread_key_create(&ah_search_key_slot, destroy_ah_search_key);
154	});
155
156	SecTransformAttributeRef search_for = pthread_getspecific(ah_search_key_slot);
157	if (!search_for)
158	{
159		search_for = makeAH((transform_attribute*)malloc(sizeof(transform_attribute)));
160		if (!search_for)
161		{
162			return NULL;
163		}
164
165		bzero(ah2ta(search_for), sizeof(transform_attribute));
166		pthread_setspecific(ah_search_key_slot, search_for);
167	}
168
169	if (!mAttributes)
170	{
171		mAttributes = CFSetCreateMutable(NULL, 0, &tasetcb);
172	}
173
174	ah2ta(search_for)->name = label;
175	SecTransformAttributeRef ah = static_cast<SecTransformAttributeRef>(const_cast<void*>(CFSetGetValue(mAttributes, search_for)));
176	if (ah == NULL && create_ok)
177	{
178		if (CFStringGetLength(label) && L'_' == CFStringGetCharacterAtIndex(label, 0) && !create_underscore_ok)
179		{
180			// Attributes with a leading _ belong to the Transform system only, not to random 3rd party transforms.
181			return NULL;
182		}
183
184		transform_attribute *ta = static_cast<transform_attribute *>(malloc(sizeof(transform_attribute)));
185		ah = makeAH(ta);
186		if (!ah)
187		{
188			return NULL;
189		}
190
191		ta->name = CFStringCreateCopy(NULL, label);
192		if (!ta->name)
193		{
194			free(ta);
195			return NULL;
196		}
197		CFIndex cnt = CFSetGetCount(mAttributes);
198		CFSetAddValue(mAttributes, ah);
199		if (CFSetGetCount(mAttributes) != cnt+1)
200		{
201			CFRelease(ta->name);
202			free(ta);
203			return NULL;
204		}
205
206		CFStringRef qname = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/%@"), dispatch_queue_get_label(this->mDispatchQueue), label);
207		CFIndex used, sz = 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(qname), kCFStringEncodingUTF8);
208		UInt8 *qnbuf = (UInt8 *)alloca(sz);
209		CFStringGetBytes(qname, CFRangeMake(0, CFStringGetLength(qname)), kCFStringEncodingUTF8, '?', FALSE, qnbuf, sz, &used);
210		qnbuf[used] = '\0';
211		ta->q = dispatch_queue_create((char*)qnbuf, NULL);
212		CFRelease(qname);
213		ta->semaphore = dispatch_semaphore_create(kMaxPendingTransactions);
214
215
216		ta->pushback_state = transform_attribute::pb_empty;
217		ta->pushback_value = NULL;
218		ta->value = NULL;
219		ta->connections = NULL;
220		ta->transform = this;
221
222		dispatch_set_target_queue(ta->q, mDispatchQueue);
223		ta->required = 0;
224		ta->requires_outbound_connection = 0;
225		ta->deferred = 0;
226		ta->stream = 0;
227		ta->ignore_while_externalizing = 0;
228		ta->has_incoming_connection = 0;
229		ta->direct_error_handling = 0;
230		ta->allow_external_sets = 0;
231		ta->has_been_deferred = 0;
232		ta->attribute_changed_block = NULL;
233		ta->attribute_validate_block = NULL;
234	}
235
236	return ah;
237}
238
239transform_attribute *Transform::getTA(SecTransformStringOrAttributeRef attrib, bool create_ok)
240{
241	SecTransformAttributeRef ah = getAH(attrib, create_ok);
242	if (ah)
243	{
244		return ah2ta(ah);
245	}
246	else
247	{
248		return NULL;
249	}
250}
251
252
253
254void Transform::TAGetAll(transform_attribute **attributes) {
255	CFSetGetValues(mAttributes, (const void**)attributes);
256	CFIndex i, n = CFSetGetCount(mAttributes);
257	for(i = 0; i < n; ++i) {
258		attributes[i] = ah2ta(attributes[i]);
259	}
260}
261
262
263
264bool Transform::HasNoOutboundConnections()
265{
266	// make an array big enough to hold all of the attributes
267	CFIndex numAttributes = CFSetGetCount(mAttributes);
268	transform_attribute* attributes[numAttributes];
269
270	TAGetAll(attributes);
271
272	// check all of the attributes
273	CFIndex i;
274	for (i = 0; i < numAttributes; ++i)
275	{
276		if (attributes[i]->connections && CFArrayGetCount(attributes[i]->connections) != 0)
277		{
278			return false;
279		}
280	}
281
282	return true;
283}
284
285
286
287bool Transform::HasNoInboundConnections()
288{
289	// make an array big enough to hold all of the attributes
290	CFIndex numAttributes = CFSetGetCount(mAttributes);
291	transform_attribute* attributes[numAttributes];
292
293	TAGetAll(attributes);
294
295	// check all of the attributes
296	CFIndex i;
297	for (i = 0; i < numAttributes; ++i)
298	{
299		if (attributes[i]->has_incoming_connection)
300		{
301			return false;
302		}
303	}
304
305	return true;
306}
307
308
309
310CFIndex Transform::GetAttributeCount()
311{
312	return CFSetGetCount(mAttributes);
313}
314
315Transform::Transform(CFStringRef transformType, CFStringRef CFobjectType) :
316	CoreFoundationObject(CFobjectType),
317	mIsActive(false),
318	mIsFinalizing(false),
319	mAlwaysSelfNotify(false),
320	mGroup(NULL),
321	mAbortError(NULL),
322	mTypeName(CFStringCreateCopy(NULL, transformType))
323{
324	mAttributes = NULL;
325	mPushedback = NULL;
326	mProcessingPushbacks = FALSE;
327
328	if (internalID == _kCFRuntimeNotATypeID) {
329		(void)SecTransformNoData();
330		internalID = CoreFoundationObject::FindObjectType(gInternalCFObjectName);
331	}
332
333	// create a name for the transform
334	char rname[10];
335	unsigned i;
336	for (i = 0; i < sizeof(rname) - 1; ++i)
337	{
338		rname[i] = RandomChar();
339	}
340
341	rname[i] = 0;
342
343	char *tname = const_cast<char*>(CFStringGetCStringPtr(transformType, kCFStringEncodingUTF8));
344	if (!tname) {
345		CFIndex sz = 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(transformType), kCFStringEncodingUTF8);
346		tname = static_cast<typeof(tname)>(alloca(sz));
347		if (tname) {
348			CFStringGetCString(transformType, tname, sz, kCFStringEncodingUTF8);
349		} else {
350			tname = const_cast<char*>("-");
351		}
352	}
353
354	char* name;
355	asprintf(&name, "%s-%s", rname, tname);
356
357	char *dqName;
358	asprintf(&dqName, "%s-%s", rname, tname);
359
360	char *aqName;
361	asprintf(&aqName, "aq-%s-%s", rname, tname);
362
363	mDispatchQueue = dispatch_queue_create(dqName, NULL);
364    dispatch_queue_set_specific(mDispatchQueue, &dispatchQueueToTransformKey, this, NULL);
365	// mActivationQueue's job in life is to be suspended until just after this transform is made active.
366	// It's primary use is when a value flowing across a connection isn't sure if the target transform is active yet.
367	mActivationQueue = dispatch_queue_create(aqName, NULL);
368	dispatch_set_target_queue(mActivationQueue, mDispatchQueue);
369	dispatch_suspend(mActivationQueue);
370	mActivationPending = dispatch_group_create();
371
372	// set up points for ABORT, DEBUG, INPUT, and OUTPUT
373	AbortAH = getAH(kSecTransformAbortAttributeName, true);
374	transform_attribute *ta = ah2ta(AbortAH);
375	ta->ignore_while_externalizing = 1;
376	CFStringRef attributeName = CFStringCreateWithCStringNoCopy(NULL, name, 0, kCFAllocatorMalloc);
377	SetAttributeNoCallback(kSecTransformTransformName, attributeName);
378	CFRelease(attributeName);
379
380	free(dqName);
381	free(aqName);
382
383	DebugAH = getAH(kSecTransformDebugAttributeName, true);
384	ah2ta(DebugAH)->ignore_while_externalizing = 1;
385
386	ta = getTA(kSecTransformInputAttributeName, true);
387	ta->required = ta->deferred = ta->stream = 1;
388	ta->allow_external_sets = 0;
389	ta->value = NULL;
390	ta->has_been_deferred = 0;
391	ta = getTA(kSecTransformOutputAttributeName, true);
392	ta->requires_outbound_connection = ta->stream = 1;
393}
394
395static void run_and_release_finalizer(void *finalizer_block)
396{
397	((dispatch_block_t)finalizer_block)();
398	Block_release(finalizer_block);
399}
400
401static void set_dispatch_finalizer(dispatch_object_t object, dispatch_block_t finalizer)
402{
403	finalizer = Block_copy(finalizer);
404	dispatch_set_context(object, finalizer);
405	dispatch_set_finalizer_f(object, run_and_release_finalizer);
406}
407
408void Transform::FinalizePhase2()
409{
410	delete this;
411}
412
413void Transform::FinalizeForClang()
414{
415	CFIndex numAttributes = CFSetGetCount(mAttributes);
416	SecTransformAttributeRef handles[numAttributes];
417	CFSetGetValues(mAttributes, (const void**)&handles);
418
419	for(CFIndex i = 0; i < numAttributes; ++i) {
420		SecTransformAttributeRef ah = handles[i];
421		transform_attribute *ta = ah2ta(ah);
422
423		set_dispatch_finalizer(ta->q, ^{
424			// NOTE: not done until all pending use of the attribute queue has ended AND retain count is zero
425			ta->transform = NULL;
426			CFRelease(ah);
427		});
428		// If there is a pending pushback the attribute queue will be suspended, and needs a kick before it can be destructed.
429		if (__sync_bool_compare_and_swap(&ta->pushback_state, transform_attribute::pb_value, transform_attribute::pb_discard)) {
430			dispatch_resume(ta->q);
431		}
432		dispatch_release(ta->q);
433	}
434
435	// We might be finalizing a transform as it is being activated, make sure that is complete before we do the rest
436    dispatch_group_notify(mActivationPending, mDispatchQueue, ^{
437        if (mActivationQueue != NULL) {
438            // This transform has not been activated (and does not have a activation pending), so we need to resume to activation queue before we can release it
439            dispatch_resume(mActivationQueue);
440            dispatch_release(mActivationQueue);
441        }
442
443        set_dispatch_finalizer(mDispatchQueue, ^{
444            // NOTE: delayed until all pending work items on the transform's queue are complete, and all of the attribute queues have been finalized, and the retain count is zero
445            FinalizePhase2();
446        });
447        dispatch_release(mDispatchQueue);
448    });
449}
450
451void Transform::Finalize()
452{
453	// When _all_ transforms in the group have been marked as finalizing we can tear down our own context without anyone else in the group sending us values
454	// (NOTE: moved block into member function as clang hits an internal error and declines to compile)
455	dispatch_block_t continue_finalization = ^{ this->FinalizeForClang(); };
456	dispatch_block_t mark_as_finalizing = ^{ this->mIsFinalizing = true; };
457
458	// Mark the transform as "finalizing" so it knows not to propagate values across connections
459    if (this == dispatch_get_specific(&dispatchQueueToTransformKey)) {
460        mark_as_finalizing();
461    } else {
462        dispatch_sync(mDispatchQueue, mark_as_finalizing);
463    }
464
465	if (mGroup) {
466        (void)transforms_assume(mGroup->mIsFinalizing);  // under retain?
467        mGroup->AddAllChildrenFinalizedCallback(mDispatchQueue, continue_finalization);
468        mGroup->ChildStartedFinalization(this);
469	} else {
470		// a "bare" transform (normally itself a group) still needs to be deconstructed
471		dispatch_async(mDispatchQueue, continue_finalization);
472	}
473}
474
475Transform::~Transform()
476{
477	CFRelease(mAttributes);
478	if (mAbortError) {
479		CFRelease(mAbortError);
480        mAbortError = NULL;
481	}
482
483	// See if we can catch anything using us after our death
484	mDispatchQueue = (dispatch_queue_t)0xdeadbeef;
485
486	CFRelease(mTypeName);
487
488	if (NULL != mPushedback)
489	{
490		CFRelease(mPushedback);
491	}
492	dispatch_release(mActivationPending);
493}
494
495CFStringRef Transform::GetName() {
496	return (CFStringRef)GetAttribute(kSecTransformTransformName);
497}
498
499CFTypeID Transform::GetCFTypeID()
500{
501	return CoreFoundationObject::FindObjectType(CFSTR("SecTransform"));
502}
503
504std::string Transform::DebugDescription()
505{
506	return CoreFoundationObject::DebugDescription() + "|SecTransform|" + StringFromCFString(this->GetName());
507}
508
509CFErrorRef Transform::SendMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type, CFTypeRef value)
510{
511	SecTransformAttributeRef ah = getAH(key, true);
512	transform_attribute *ta = ah2ta(ah);
513	switch (type)
514	{
515		case kSecTransformMetaAttributeRequired:
516			ta->required = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0;
517			break;
518
519		case kSecTransformMetaAttributeRequiresOutboundConnection:
520			ta->requires_outbound_connection = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0;
521			break;
522
523		case kSecTransformMetaAttributeDeferred:
524			ta->deferred = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0;
525			break;
526
527		case kSecTransformMetaAttributeStream:
528			ta->stream = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0;
529			break;
530
531		case kSecTransformMetaAttributeHasOutboundConnections:
532			return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeHasOutboundConnections for %@ (or any other attribute)", ah);
533
534		case kSecTransformMetaAttributeHasInboundConnection:
535			return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeHasInboundConnection for %@ (or any other attribute)", ah);
536
537		case kSecTransformMetaAttributeCanCycle:
538			return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "kSecTransformMetaAttributeCanCycle not yet supported (%@)", ah);
539
540		case kSecTransformMetaAttributeExternalize:
541			ta->ignore_while_externalizing = CFBooleanGetValue((CFBooleanRef)value) ? 0 : 1;
542			break;
543
544		case kSecTransformMetaAttributeValue:
545			return SetAttributeNoCallback(ah, value);
546
547		case kSecTransformMetaAttributeRef:
548			return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeRef for %@ (or any other attribute)", ah);
549
550		case kSecTransformMetaAttributeName:
551			return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeName for %@ (or any other attribute)", ah);
552
553		default:
554			return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set unknown meta attribute #%d to %@ on %@", type, value, key);
555	}
556
557	return NULL;
558}
559
560CFTypeRef Transform::GetMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type) {
561	SecTransformAttributeRef ah = getAH(key, true);
562	transform_attribute *ta = ah2ta(ah);
563	switch (type) {
564		case kSecTransformMetaAttributeRequired:
565			return (CFTypeRef)(ta->required ? kCFBooleanTrue : kCFBooleanFalse);
566		case kSecTransformMetaAttributeRequiresOutboundConnection:
567			return (CFTypeRef)(ta->requires_outbound_connection ? kCFBooleanTrue : kCFBooleanFalse);
568		case kSecTransformMetaAttributeDeferred:
569			return (CFTypeRef)(ta->deferred ? kCFBooleanTrue : kCFBooleanFalse);
570		case kSecTransformMetaAttributeStream:
571			return (CFTypeRef)(ta->stream ? kCFBooleanTrue : kCFBooleanFalse);
572		case kSecTransformMetaAttributeHasOutboundConnections:
573			return (CFTypeRef)((ta->connections && CFArrayGetCount(ta->connections)) ? kCFBooleanTrue : kCFBooleanFalse);
574		case kSecTransformMetaAttributeHasInboundConnection:
575			return (CFTypeRef)(ta->has_incoming_connection ? kCFBooleanTrue : kCFBooleanFalse);
576		case kSecTransformMetaAttributeCanCycle:
577			return (CFTypeRef)kCFBooleanFalse;
578		case kSecTransformMetaAttributeExternalize:
579			return (CFTypeRef)(ta->ignore_while_externalizing ? kCFBooleanFalse : kCFBooleanTrue);
580		case kSecTransformMetaAttributeRef:
581			return ah;
582		case kSecTransformMetaAttributeValue:
583			return ta->value;
584		case kSecTransformMetaAttributeName:
585			return ta->name;
586		default:
587			return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't get unknown meta attribute #%d from %@", type, key);
588			break;
589	}
590
591	return NULL;
592}
593
594
595
596CFErrorRef Transform::RefactorErrorToIncludeAbortingTransform(CFErrorRef sourceError)
597{
598	// pull apart the error
599	CFIndex code = CFErrorGetCode(sourceError);
600	CFStringRef domain = CFErrorGetDomain(sourceError);
601	CFDictionaryRef oldUserInfo = CFErrorCopyUserInfo(sourceError);
602	CFMutableDictionaryRef userInfo = CFDictionaryCreateMutableCopy(NULL, 0, oldUserInfo);
603	CFRelease(oldUserInfo);
604
605	// add the new key and value to the dictionary
606	CFDictionaryAddValue(userInfo, kSecTransformAbortOriginatorKey, GetCFObject());
607
608	// make a new CFError
609	CFErrorRef newError = CFErrorCreate(NULL, domain, code, userInfo);
610	CFRelease(userInfo);
611	return newError;
612}
613
614// NOTE: If called prior to execution will schedule a later call to AbortAllTransforms
615void Transform::AbortJustThisTransform(CFErrorRef abortErr)
616{
617    (void)transforms_assume(abortErr);
618    (void)transforms_assume(dispatch_get_specific(&dispatchQueueToTransformKey) == this);
619
620    Boolean wasActive = mIsActive;
621
622    if (OSAtomicCompareAndSwapPtr(NULL, (void *)abortErr, (void**)&mAbortError)) {
623		// send an abort message to the attribute so that it can shut down
624		// note that this bypasses the normal processes.  The message sent is a notification
625		// that things aren't working well any more, the transform cannot make any other assumption.
626
627        // mAbortError is released in the destructor which is triggered (in part)
628        // by the dispatch queue finalizer so we don't need a retain/release of
629        // abortErr for the abortAction block, but we do need to retain it
630        // here to match with the release by the destructor.
631        CFRetain(abortErr);
632
633		dispatch_block_t abortAction = ^{
634            // This actually makes the abort happen, it needs to run on the transform's queue while the
635            // transform is executing.
636
637            if (!wasActive) {
638                // When this abort was first processed we were not executing, so
639                // additional transforms may have been added to our group (indeed,
640                // we may not have had a group at all), so we need to let everyone
641                // know about the problem.   This will end up letting ourself (and
642                // maybe some others) know an additional time, but the CompareAndSwap
643                // prevents that from being an issue.
644                this->AbortAllTransforms(abortErr);
645            }
646
647            SecTransformAttributeRef GCC_BUG_WORKAROUND inputAttributeHandle = getAH(kSecTransformInputAttributeName, false);
648            // Calling AttributeChanged directly lets an error "skip ahead" of the input queue,
649            // and even execute if the input queue is suspended pending pushback retries.
650            AttributeChanged(inputAttributeHandle, abortErr);
651            try_pushbacks();
652        };
653
654        if (mIsActive) {
655            // This transform is running, so we use the normal queue (which we are
656            // already executing on)
657            abortAction();
658        } else {
659            // This transform hasn't run yet, do the work on the activation queue
660            // so it happens as soon as the transforms starts executing.
661            dispatch_async(mActivationQueue, abortAction);
662        }
663	} else {
664        Debug("%@ set to %@ while processing ABORT=%@, this extra set will be ignored", AbortAH, abortErr, mAbortError);
665    }
666}
667
668// abort all transforms in the root group & below
669void Transform::AbortAllTransforms(CFTypeRef err)
670{
671	Debug("%@ set to %@, aborting\n", AbortAH, err);
672	CFErrorRef error = NULL;
673
674	CFTypeRef replacementErr = NULL;
675
676	if (CFGetTypeID(err) != CFErrorGetTypeID())
677	{
678		replacementErr = err = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "ABORT set to a %@ (%@) not a %@", CFCopyTypeIDDescription(CFGetTypeID(err)), err, CFCopyTypeIDDescription(CFErrorGetTypeID()));
679	}
680
681	error = RefactorErrorToIncludeAbortingTransform((CFErrorRef)err);
682
683	if (replacementErr)
684	{
685		CFRelease(replacementErr);
686	}
687
688    GroupTransform *root = GetRootGroup();
689	if (root)
690	{
691		// tell everyone in the (root) group to "AbortJustThisTransform"
692        dispatch_group_t all_aborted = dispatch_group_create();
693        root->ForAllNodesAsync(false, all_aborted, ^(Transform* t){
694            t->AbortJustThisTransform(error);
695        });
696        dispatch_group_notify(all_aborted, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) {
697            CFRelease(error);
698            dispatch_release(all_aborted);
699        });
700	}
701	else
702	{
703		// We are everyone so we AbortJustThisTransform "directly"
704        // NOTE: this can only happen prior to execution (execution always happens in a group)
705        (void)transforms_assume_zero(mIsActive);
706		this->AbortJustThisTransform(error);
707	}
708}
709
710
711
712CFErrorRef Transform::Disconnect(Transform* destinationTransform, CFStringRef myKey, CFStringRef hisKey)
713{
714	//CFTypeRef thisTransform = (SecTransformRef) GetCFObject();
715
716	// find this transform in the backlinks for the destination
717	CFIndex i;
718
719	// now remove the link in the transform dictionary
720	transform_attribute *src = getTA(myKey, true);
721	SecTransformAttributeRef dst = destinationTransform->getAH(hisKey);
722
723	if (src->connections == NULL)
724	{
725		return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Cannot find transform in destination.");
726	}
727
728	CFIndex numConnections = CFArrayGetCount(src->connections);
729	for (i = 0; i < numConnections; ++i)
730	{
731		if (CFArrayGetValueAtIndex(src->connections, i) == dst)
732		{
733			CFArrayRemoveValueAtIndex(src->connections, i);
734			numConnections = CFArrayGetCount(src->connections);
735		}
736
737		// clear the has_incoming_connection bit in the destination.  We can do this because inputs can have only one connection.
738		transform_attribute* dstTA = ah2ta(dst);
739		dstTA->has_incoming_connection = false;
740	}
741
742	if (HasNoInboundConnections() && HasNoOutboundConnections())
743	{
744		// we have been orphaned, just remove us
745		mGroup->RemoveMemberFromGroup(GetCFObject());
746		mGroup = NULL;
747	}
748
749	return NULL;
750}
751
752
753
754CFErrorRef Transform::Connect(GroupTransform *group, Transform* destinationTransform, CFStringRef destAttr, CFStringRef srcAttr)
755{
756	if (group == NULL)
757	{
758		CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make connections without a specific group (do not call with group = NULL)");
759		return err;
760	}
761
762    GroupTransform *newSourceGroup = mGroup;
763    GroupTransform *newDestinationGroup = destinationTransform->mGroup;
764
765	if (mGroup == NULL || mGroup == this)
766	{
767		newSourceGroup = group;
768	}
769
770	if (destinationTransform->mGroup == NULL || destinationTransform->mGroup == destinationTransform)
771	{
772		newDestinationGroup = group;
773	}
774
775	if (newSourceGroup != newDestinationGroup && mGroup)
776	{
777        CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make connections between transforms in different groups (%@ is in %@, %@ is in %@)", GetName(), newSourceGroup->GetName(), destinationTransform->GetName(), newDestinationGroup->GetName());
778		return err;
779	}
780
781    if (!validConnectionPoint(srcAttr)) {
782        CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make a connection from non-exported attribute %@ of %@", srcAttr, this->GetName());
783        return err;
784    }
785    if (!destinationTransform->validConnectionPoint(destAttr)) {
786        CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make a connection to non-exported attribute %@ of %@", destAttr, destinationTransform->GetName());
787        return err;
788    }
789
790    mGroup = newSourceGroup;
791    destinationTransform->mGroup = newDestinationGroup;
792
793	// NOTE: this fails on OOM
794	group->AddMemberToGroup(this->GetCFObject());
795	group->AddMemberToGroup(destinationTransform->GetCFObject());
796
797	transform_attribute *src = this->getTA(srcAttr, true);
798	SecTransformAttributeRef dst = destinationTransform->getAH(destAttr);
799
800	if (!src->connections)
801	{
802		src->connections = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
803	}
804	CFArrayAppendValue(src->connections, dst);
805
806	ah2ta(dst)->has_incoming_connection = 1;
807
808	return NULL;
809}
810
811
812bool Transform::validConnectionPoint(CFStringRef attributeName)
813{
814    return true;
815}
816
817// NoCallback == don't call this transform's Do function, but DO call the Do functions of connected attributes
818// SetAttribute eventually calls SetAttributeNoCallback
819CFErrorRef Transform::SetAttributeNoCallback(SecTransformStringOrAttributeRef key, CFTypeRef value)
820{
821	SecTransformAttributeRef ah = getAH(key, true);
822	if (!ah)
823	{
824		abort();
825	}
826	transform_attribute *ta = ah2ta(ah);
827
828	if (ah == AbortAH && value && (mIsActive || !ta->deferred))
829	{
830		AbortAllTransforms(value);
831		return CreateSecTransformErrorRef(kSecTransformErrorAbortInProgress, "Abort started");
832	}
833
834	bool do_propagate = true;
835
836	if (!ta->has_been_deferred)
837	{
838		bool doNotRetain = false;
839
840		if (value)
841		{
842			CFStringRef name = ta->name;
843			if (CFGetTypeID(value) == CFReadStreamGetTypeID())
844			{
845				CFTypeRef src = StreamSource::Make((CFReadStreamRef) value, this, name);
846				value = src;
847				do_propagate = false;
848				ta->has_been_deferred = 1;
849				doNotRetain = true;
850			}
851			else if (ta->deferred && !mIsActive)
852			{
853				if (ta->deferred)
854				{
855					Debug("%@ deferred value=%p\n", ah, value);
856				}
857
858				CFTypeRef src = SingleShotSource::Make(value, this, name);
859				ta->has_been_deferred = 1;
860
861				// the old value will be release when Transform::Do terminates
862
863				value = src;
864				do_propagate = false;
865				doNotRetain = true;
866			}
867			else
868			{
869				ta->has_been_deferred = 0;
870			}
871		}
872
873		if (ta->value != value) {
874			if (value && !doNotRetain) {
875				CFRetain(value);
876			}
877			if (ta->value) {
878				CFRelease(ta->value);
879			}
880		}
881
882		ta->value = value;
883	}
884
885	// propagate the changes out to all connections
886	if (ta->connections && mIsActive && do_propagate && !(mAbortError || mIsFinalizing))
887	{
888		Debug("Propagating from %@ to %@\n", ah, ta->connections);
889		CFIndex i, numConnections = CFArrayGetCount(ta->connections);
890		for(i = 0; i < numConnections; ++i) {
891			SecTransformAttributeRef ah = static_cast<SecTransformAttributeRef>(const_cast<void *>(CFArrayGetValueAtIndex(ta->connections, i)));
892			Transform *tt = ah2ta(ah)->transform;
893			if (NULL != tt)
894			{
895				if (tt->mIsActive)
896				{
897					tt->SetAttribute(ah, value);
898				}
899				else
900				{
901					dispatch_block_t setAttribute = ^{
902                        tt->SetAttribute(ah, value);
903                    };
904                    // Here the target queue might not be activated yet, we can't
905                    // look directly at the other transform's ActivationQueue as
906                    // it might activate (or Finalize!) as we look, so just ask
907                    // the other transform to deal with it.
908                    dispatch_async(ah2ta(ah)->q, ^(void) {
909                        // This time we are on the right queue to know this is the real deal
910                        if (tt->mIsActive) {
911                            setAttribute();
912                        } else {
913                            dispatch_async(ah2ta(ah)->transform->mActivationQueue, setAttribute);
914                        }
915                    });
916				}
917			}
918		}
919	}
920
921	return NULL;
922}
923
924// external sets normally fail if the transform is running
925CFErrorRef Transform::ExternalSetAttribute(CFTypeRef key, CFTypeRef value)
926{
927	if (!mIsActive)
928	{
929		return this->SetAttribute(key, value);
930	}
931	else
932	{
933		SecTransformAttributeRef ah = getAH(key, false);
934		if (ah != NULL && ah2ta(ah)->allow_external_sets)
935		{
936			return this->SetAttribute(static_cast<CFTypeRef>(ah), value);
937		}
938		else
939		{
940			return CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "%@ can not be set while %@ is executing", ah, this->GetName());
941		}
942	}
943}
944
945
946// queue up the setting of the key and value
947CFErrorRef Transform::SetAttribute(CFTypeRef key, CFTypeRef value)
948{
949	if (mAbortError)
950	{
951		return CreateSecTransformErrorRef(kSecTransformErrorAborted, "ABORT has been sent to the transform (%@)", mAbortError);
952	}
953
954	// queue up the setting of the key and value
955	SecTransformAttributeRef ah;
956	if (CFGetTypeID(key) == transform_attribute::cftype)
957	{
958		ah = key;
959	}
960	else if (CFGetTypeID(key) == CFStringGetTypeID())
961	{
962		ah = getAH(static_cast<CFStringRef>(key));
963		if (!ah)
964		{
965			return CreateSecTransformErrorRef(kSecTransformErrorUnsupportedAttribute, "Can't set attribute %@ in transform %@", key, GetName());
966		}
967	}
968	else
969	{
970		return CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "Transform::SetAttribute called with %@, requires a string or an AttributeHandle", key);
971	}
972
973	// Do this after the error check above so we don't leak
974	if (value != NULL)
975	{
976		CFRetain(value); // if we use dispatch_async we need to own the value (the matching release is in the set block)
977	}
978
979
980	transform_attribute *ta = ah2ta(ah);
981
982	dispatch_block_t set = ^{
983		Do(ah, value);
984
985		dispatch_semaphore_signal(ta->semaphore);
986
987		if (value != NULL)
988		{
989			CFRelease(value);
990		}
991	};
992
993
994	// when the transform is active, set attributes asynchronously.  Otherwise, we are doing
995	// initialization and must wait for the operation to complete.
996	if (mIsActive)
997	{
998		dispatch_async(ta->q, set);
999	}
1000	else
1001	{
1002		dispatch_sync(ta->q, set);
1003	}
1004	if (dispatch_semaphore_wait(ta->semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC))) {
1005		Debug("Send from %@ to %@ is still waiting\n", GetName(), ah);
1006		dispatch_semaphore_wait(ta->semaphore, DISPATCH_TIME_FOREVER);
1007	}
1008
1009	// Return the best available status (which will be NULL if we haven't aborted, or stated an
1010	// intent to abort when execution starts)
1011	//
1012	// The value of the ABORT attribute can differ from mAbortError, first if a transform is aborted
1013	// prior to running the general abort mechanic is deferred until execution.   Second during
1014	// execution the abort logic avoids most of the normal processing.   Third, and most importantly
1015	// during an abort the exact error that gets generated will differ from the value sent to ABORT
1016	// (for example if a non-CFError was sent...plus even if it was a CFError we annotate that error).
1017
1018	return mAbortError;
1019}
1020
1021CFErrorRef Transform::SendAttribute(SecTransformStringOrAttributeRef key, CFTypeRef value)
1022{
1023	return SetAttributeNoCallback(key, value);
1024}
1025
1026
1027
1028CFTypeRef Transform::GetAttribute(SecTransformStringOrAttributeRef key)
1029{
1030	struct transform_attribute *ta = getTA(key, false);
1031	if (ta == NULL || ta->value == NULL) {
1032		return NULL;
1033	}
1034
1035	if (CFGetTypeID(ta->value) == internalID)
1036	{
1037		// this is one of our internal objects, so get the value from it
1038		Source* source = (Source*) CoreFoundationHolder::ObjectFromCFType(ta->value);
1039		return source->GetValue();
1040	}
1041	else
1042	{
1043		return ta->value;
1044	}
1045}
1046
1047CFErrorRef Transform::Pushback(SecTransformAttributeRef ah, CFTypeRef value)
1048{
1049	CFErrorRef result = NULL;
1050	transform_attribute *ta = ah2ta(ah);
1051	if (!(ta->pushback_state == transform_attribute::pb_empty || ta->pushback_state == transform_attribute::pb_repush))
1052	{
1053		CFErrorRef error = fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidOperation, CFSTR("Can not pushback new value until old value has been processed"));
1054		SetAttribute(kSecTransformAbortAttributeName, error);
1055		return error;
1056	}
1057	if (value == NULL && ta->pushback_value == NULL && ta->pushback_state == transform_attribute::pb_repush)
1058	{
1059		ta->pushback_state = transform_attribute::pb_presented_once;
1060	} else
1061	{
1062		ta->pushback_state = transform_attribute::pb_value;
1063	}
1064	if (value)
1065	{
1066		CFRetain(value);
1067	}
1068	ta->pushback_value = value;
1069	dispatch_suspend(ta->q);
1070	if (!mPushedback)
1071	{
1072		mPushedback = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1073	}
1074	CFArrayAppendValue(mPushedback, ah);
1075	return result;
1076}
1077
1078void Transform::try_pushbacks() {
1079	if (!mPushedback || !CFArrayGetCount(mPushedback)) {
1080		mProcessingPushbacks = FALSE;
1081		return;
1082	}
1083
1084	CFArrayRef pb = (CFArrayRef)mPushedback;
1085	mPushedback = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1086	CFIndex i, n = CFArrayGetCount(pb);
1087	int succeeded = 0;
1088	for(i = 0; i < n; ++i)
1089	{
1090		SecTransformAttributeRef ah = CFArrayGetValueAtIndex(pb, i);
1091		transform_attribute *ta = ah2ta(ah);
1092		ta->pushback_state = transform_attribute::pb_repush;
1093		CFTypeRef v = ta->pushback_value;
1094		ta->pushback_value = NULL;
1095		Do(ah, v);
1096		if (v)
1097		{
1098			CFRelease(v);
1099		}
1100		if (ta->pushback_state == transform_attribute::pb_repush) {
1101			ta->pushback_state = transform_attribute::pb_empty;
1102			succeeded++;
1103		}
1104		// NOTE: a successful repush needs the queue unsuspended so it can run.
1105		// A failed repush has suspended the queue an additional time, so we
1106		// still need to resume it.
1107		dispatch_resume(ta->q);
1108	}
1109
1110	CFRelease(pb);
1111
1112	if (succeeded && CFArrayGetCount(mPushedback)) {
1113		// some attribute changed while we proceeded the last batch of pushbacks, so any "new" pushbacks are eligible to run again.
1114		// In theory the ones that were pushed after the last success don't need to be re-run but that isn't a big deal.
1115		dispatch_async(mDispatchQueue, ^{ try_pushbacks(); });
1116	} else {
1117		mProcessingPushbacks = FALSE;
1118	}
1119}
1120
1121void Transform::Debug(const char *cfmt, ...) {
1122	CFTypeRef d = ah2ta(DebugAH)->value;
1123	if (d) {
1124		CFWriteStreamRef out = NULL;
1125		if (CFGetTypeID(d) == CFWriteStreamGetTypeID()) {
1126			out = (CFWriteStreamRef)d;
1127		} else {
1128			static dispatch_once_t once;
1129			static CFWriteStreamRef StdErrWriteStream;
1130			dispatch_once(&once, ^{
1131				auto GCC_BUG_WORKAROUND CFURLRef GCC_BUG_WORKAROUND p = CFURLCreateWithFileSystemPath(NULL, CFSTR("/dev/stderr"), kCFURLPOSIXPathStyle, FALSE);
1132				StdErrWriteStream = CFWriteStreamCreateWithFile(NULL, p);
1133				CFWriteStreamOpen(StdErrWriteStream);
1134				CFRelease(p);
1135			});
1136			out = StdErrWriteStream;
1137		}
1138
1139		va_list ap;
1140		va_start(ap, cfmt);
1141
1142		CFStringRef fmt = CFStringCreateWithCString(NULL, cfmt, kCFStringEncodingUTF8);
1143		CFStringRef str = CFStringCreateWithFormatAndArguments(NULL, NULL, fmt, ap);
1144		CFRelease(fmt);
1145		va_end(ap);
1146
1147
1148		CFIndex sz = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8);
1149		sz += 1;
1150		CFIndex used = 0;
1151		unsigned char *buf;
1152		bool needs_free = true;
1153		buf = (unsigned char*)malloc(sz);
1154		if (buf) {
1155			CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, '?', FALSE, buf, sz, &used);
1156		} else {
1157			buf = (unsigned char *)"malloc failure during Transform::Debug\n";
1158			needs_free = false;
1159		}
1160
1161		static dispatch_once_t once;
1162		static dispatch_queue_t print_q;
1163		dispatch_once(&once, ^{
1164			print_q = dispatch_queue_create("com.apple.security.debug.print_queue", 0);
1165			dispatch_set_target_queue((dispatch_object_t)print_q, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
1166		});
1167
1168		dispatch_async(print_q, ^{
1169			CFWriteStreamWrite(out, buf, used);
1170			if (needs_free) {
1171				free(buf);
1172			}
1173		});
1174
1175		CFRelease(str);
1176	}
1177}
1178
1179void Transform::Do(SecTransformAttributeRef ah, CFTypeRef value)
1180{
1181	transform_attribute *ta = ah2ta(ah);
1182	if (ta->pushback_state == transform_attribute::pb_discard)
1183	{
1184		return;
1185	}
1186	(void)transforms_assume(dispatch_get_current_queue() == ((ta->pushback_state == transform_attribute::pb_repush) ? mDispatchQueue : ta->q));
1187
1188	if (mIsFinalizing)
1189	{
1190		Debug("Ignoring value %p sent to %@ (on queue %s) during finalization", value, ah, dispatch_queue_get_label(dispatch_get_current_queue()));
1191		return;
1192	}
1193
1194	SetAttributeNoCallback(ah, value);
1195    // While an abort is in progress things can get into bad
1196    // states if we allow normal processing so we throw anything
1197    // on the floor except CFErrorRef or NULL vales sent to
1198    // ABORT or INPUT (we need to process them to let the
1199    // transform shut down correctly)
1200	if (mAbortError && (!(ah == this->AbortAH || ah == getTA(CFSTR("INPUT"), true)) && (value == NULL || CFGetTypeID(value) != CFErrorGetTypeID())))
1201	{
1202		if (value) {
1203            Debug("Ignoring value (%@) sent to %@ during abort\n", value, ah);
1204        } else {
1205            Debug("Ignoring NULL sent to %@ during abort\n", ah);
1206        }
1207		return;
1208	}
1209
1210	if (mIsActive || (mAlwaysSelfNotify && !ta->deferred))
1211	{
1212		Debug("AttributeChanged: %@ (%s) = %@\n", ah, mIsActive ? "is executing" : "self notify set", value ? value : (CFTypeRef)CFSTR("(NULL)"));
1213		AttributeChanged(ah, value);
1214	}
1215
1216	if (mPushedback && CFArrayGetCount(mPushedback) && !mProcessingPushbacks)
1217	{
1218		Debug("will process pushbacks (%@) later\n", mPushedback);
1219		mProcessingPushbacks = TRUE;
1220		dispatch_async(mDispatchQueue, ^{ try_pushbacks(); });
1221	}
1222
1223	return;
1224}
1225
1226
1227void Transform::AttributeChanged(CFStringRef name, CFTypeRef value)
1228{
1229}
1230
1231void Transform::AttributeChanged(SecTransformAttributeRef ah, CFTypeRef value)
1232{
1233	AttributeChanged(ah2ta(ah)->name, value);
1234}
1235
1236CFArrayRef Transform::GetAllAH() {
1237	CFIndex cnt = CFSetGetCount(mAttributes);
1238	const void **values = (const void **)alloca(sizeof(void*)*cnt);
1239	CFSetGetValues(mAttributes, values);
1240	return CFArrayCreate(NULL, values, cnt, &kCFTypeArrayCallBacks);
1241}
1242
1243CFTypeRef Transform::Execute(dispatch_queue_t deliveryQueue, SecMessageBlock deliveryBlock, CFErrorRef* errorRef)
1244{
1245	if (!mGroup)
1246	{
1247		CFTypeRef g = GroupTransform::Make();
1248		mGroup = (GroupTransform*)CoreFoundationHolder::ObjectFromCFType(g);
1249		mGroup->AddMemberToGroup(this->GetCFObject());
1250		SecMessageBlock smb = ^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
1251		{
1252			deliveryBlock(message, error, isFinal);
1253			if (isFinal)
1254			{
1255				dispatch_async(this->mDispatchQueue, ^{
1256					CFRelease(g);
1257				});
1258			}
1259		};
1260
1261		CFTypeRef ret = this->Execute(deliveryQueue, deliveryBlock ? smb : (SecMessageBlock) NULL, errorRef);
1262
1263		if (!deliveryBlock)
1264		{
1265			CFRelease(g);
1266		}
1267
1268		return ret;
1269	}
1270
1271	if (mIsActive)
1272	{
1273		if (errorRef)
1274		{
1275			*errorRef = CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform has already executed, it may not be executed again.", GetName());
1276		}
1277
1278		return NULL;
1279	}
1280
1281	// Do a retain on our parent since we are using it
1282    GroupTransform *rootGroup = GetRootGroup();
1283	CFRetain(rootGroup->GetCFObject());
1284
1285	CFTypeRef result = NULL;
1286
1287	CFTypeRef monitorRef =  BlockMonitor::Make(deliveryQueue, deliveryBlock);
1288
1289	__block CFStringRef outputAttached = NULL;
1290
1291	dispatch_queue_t p2 = dispatch_queue_create("activate phase2", NULL);
1292	dispatch_queue_t p3 = dispatch_queue_create("activate phase3", NULL);
1293	dispatch_suspend(p2);
1294	dispatch_suspend(p3);
1295	// walk the transform, doing phase1 activating as we go, and queueing phase2 and phase3 work
1296	CFErrorRef temp = TraverseTransform(NULL, ^(Transform *t){
1297        return t->ExecuteOperation(outputAttached, (SecMonitorRef)monitorRef, p2, p3);
1298	});
1299	// ExecuteOperation is not called for the outer group, so we need to manually set mISActive for it.
1300	rootGroup->mIsActive = true;
1301    rootGroup->StartingExecutionInGroup();
1302	dispatch_resume(p2);
1303	dispatch_sync(p2, ^{ dispatch_resume(p3); });
1304	dispatch_sync(p3, ^{ dispatch_release(p2); });
1305	dispatch_release(p3);
1306
1307	if (errorRef)
1308	{
1309		*errorRef = temp;
1310	}
1311	if (temp) {
1312        // It is safe to keep the monitors attached, because it is invalid to try to execute again, BUT
1313        // we do need to release the reference to the group that the monitor would normally release
1314        // when it processes the final message.
1315        CFRelease(rootGroup->GetCFObject());
1316        CFRelease(monitorRef);
1317        rootGroup->StartedExecutionInGroup(false);
1318        return NULL;
1319	}
1320
1321	dispatch_group_t initialized = dispatch_group_create();
1322	rootGroup->ForAllNodesAsync(true, initialized, ^(Transform*t) {
1323        t->Initialize();
1324	});
1325
1326	dispatch_group_notify(initialized, rootGroup->mDispatchQueue, ^{
1327		dispatch_release(initialized);
1328		dispatch_group_t activated = dispatch_group_create();
1329		dispatch_group_enter(activated);
1330		dispatch_async(rootGroup->mDispatchQueue, ^{
1331			rootGroup->ForAllNodesAsync(true, activated, ^(Transform*t) {
1332				t->ActivateInputs();
1333			});
1334			dispatch_group_leave(activated);
1335		});
1336		dispatch_group_notify(activated, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1337			dispatch_release(activated);
1338			// once we have been activated (but not before!), the monitor belongs to the group, and we can drop our claim
1339			CFRelease(monitorRef);
1340			rootGroup->StartedExecutionInGroup(true);
1341		});
1342	});
1343
1344	return result;
1345}
1346
1347
1348void Transform::Initialize()
1349{
1350}
1351
1352static void ActivateInputs_set(const void *v, void *unused) {
1353	transform_attribute *ta = static_cast<transform_attribute *>(ah2ta(const_cast<void *>(v)));
1354	if (ta->value && internalID == CFGetTypeID(ta->value)) {
1355		Source* s = (Source*) CoreFoundationHolder::ObjectFromCFType(ta->value);
1356		s->Activate();
1357	}
1358}
1359
1360void Transform::ActivateInputs()
1361{
1362	(void)transforms_assume_zero(mIsActive && this != dispatch_get_specific(&dispatchQueueToTransformKey));
1363
1364	// now run all of the forward links
1365	if (!mIsFinalizing) {
1366		CFSetApplyFunction(mAttributes, ActivateInputs_set, NULL);
1367	}
1368}
1369
1370CFErrorRef Transform::ForAllNodes(bool parallel, bool includeOwningGroup, Transform::TransformOperation op)
1371{
1372    GroupTransform *g = GetRootGroup();
1373	if (g) {
1374		return g->ForAllNodes(parallel, includeOwningGroup, op);
1375	} else {
1376		return op(this);
1377	}
1378}
1379
1380CFErrorRef Transform::TraverseTransform(CFMutableSetRef visited, TransformOperation t)
1381{
1382	return ForAllNodes(true, true, t);
1383}
1384
1385CFErrorRef Transform::ExecuteOperation(CFStringRef &outputAttached, SecMonitorRef output, dispatch_queue_t phase2, dispatch_queue_t phase3)
1386{
1387	if (!mGroup) {
1388        // top level groups are special, and don't go through this path.
1389        return NULL;
1390    }
1391
1392    if (!TransformCanExecute())
1393	{
1394		// oops, this transform isn't ready to go
1395		return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly, "The transform %@ was not ready for execution.", GetName());
1396	}
1397
1398	// check to see if required attributes are connected or set
1399	CFIndex i, numAttributes = CFSetGetCount(mAttributes);
1400	transform_attribute **attributes = (transform_attribute **)alloca(numAttributes * sizeof(transform_attribute *));
1401	TAGetAll(attributes);
1402	CFMutableArrayRef still_need = NULL;
1403	for(i = 0; i < numAttributes; ++i) {
1404		transform_attribute *ta = attributes[i];
1405		if (ta->required && ta->value == NULL && !ta->has_incoming_connection) {
1406			if (!still_need) {
1407				still_need = CFArrayCreateMutable(NULL, i, &kCFTypeArrayCallBacks);
1408			}
1409			CFArrayAppendValue(still_need, ta->name);
1410		}
1411	}
1412	if (still_need) {
1413		CFStringRef elist = CFStringCreateByCombiningStrings(NULL, still_need, CFSTR(", "));
1414		CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorMissingParameter, "Can not execute %@, missing required attributes: %@", GetName(), elist);
1415		CFRelease(elist);
1416		CFRelease(still_need);
1417		return err;
1418	}
1419
1420	// see if we can attach our output here (note mAttributes may have changed)
1421	numAttributes = CFSetGetCount(mAttributes);
1422	attributes = (transform_attribute **)alloca(numAttributes * sizeof(transform_attribute *));
1423	TAGetAll(attributes);
1424	for (i = 0; i < numAttributes; ++i)
1425	{
1426		transform_attribute *ta = attributes[i];
1427		CFIndex arraySize = ta->connections ? CFArrayGetCount(ta->connections) : 0;
1428		if (arraySize == 0 && ta->requires_outbound_connection)
1429		{
1430			if (CFStringCompare(ta->name, kSecTransformOutputAttributeName, 0) == kCFCompareEqualTo) {
1431				// this is a place where we can hook up our output -- maybe
1432				if (outputAttached)
1433				{
1434					// oops, we've already done that.
1435					return CreateSecTransformErrorRef(kSecTransformErrorMoreThanOneOutput, "Both %@ and %@ have loose outputs, attach one to something", outputAttached, ta->transform->GetName());
1436				}
1437				// Delay the connect until after ForAllNodes returns
1438				dispatch_async(phase2, ^{
1439					SecTransformConnectTransformsInternal(mGroup->GetCFObject(),
1440														  GetCFObject(), kSecTransformOutputAttributeName,
1441														  output, kSecTransformInputAttributeName);
1442				});
1443				outputAttached = ta->transform->GetName();
1444
1445				// activate the attached monitor
1446				Monitor* m = (Monitor*) CoreFoundationHolder::ObjectFromCFType(output);
1447				m->mIsActive = true;
1448
1449				// add the monitor to the output so that it doesn't get activated twice
1450			} else {
1451				return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly, "Attribute %@ (in %@) requires an outbound connection and doesn't have one", ta->name, GetName());
1452			}
1453
1454			break;
1455		}
1456	}
1457
1458	// Delay activation until after the Monitor is connected
1459	dispatch_async(phase3, ^{
1460		phase3Activation();
1461	});
1462
1463	return NULL;
1464}
1465
1466
1467
1468void Transform::DoPhase3Activation()
1469{
1470    this->mIsActive = true;
1471    // execution has now truly started ("mIsActive is true")
1472    CFErrorRef initError = TransformStartingExecution();
1473    if (initError)
1474    {
1475        // Oops, now execution is about to grind to a halt
1476        this->SendAttribute(AbortAH, initError);
1477    }
1478
1479    dispatch_resume(this->mActivationQueue);
1480    dispatch_group_async(this->mActivationPending, this->mActivationQueue, ^{
1481        dispatch_release(this->mActivationQueue);
1482        this->mActivationQueue = NULL;
1483    });
1484}
1485
1486
1487
1488// This would be best expressed as a block, but we seem to run into compiler errors
1489void Transform::phase3Activation()
1490{
1491    dispatch_async(this->mDispatchQueue, ^
1492    {
1493        DoPhase3Activation();
1494    });
1495}
1496
1497
1498Boolean Transform::TransformCanExecute()
1499{
1500	return true;
1501}
1502
1503
1504
1505CFErrorRef Transform::TransformStartingExecution()
1506{
1507	return NULL;
1508}
1509
1510
1511
1512bool Transform::IsExternalizable()
1513{
1514	return true;
1515}
1516
1517static const void *CFTypeOrNULLRetain(CFAllocatorRef allocator, const void *value) {
1518	if (value != NULL) {
1519		return CFRetain(value);
1520	} else {
1521		return value;
1522	}
1523}
1524
1525static void CFTypeOrNULLRelease(CFAllocatorRef allocator, const void *value) {
1526	if (value != NULL) {
1527		CFRelease(value);
1528	}
1529}
1530
1531static CFStringRef CFTypeOrNULLCopyDescription (const void *value) {
1532	if (value != NULL) {
1533		return CFCopyDescription(value);
1534	} else {
1535		return CFSTR("NULL");
1536	}
1537}
1538
1539static Boolean CFTypeOrNULLEqual(const void *value1, const void *value2) {
1540	if (value1 == NULL && value2 == NULL) {
1541		return TRUE;
1542	} else {
1543		if (value1 == NULL || value2 == NULL) {
1544			return FALSE;
1545		} else {
1546			return CFEqual(value1, value2);
1547		}
1548	}
1549}
1550
1551// Returns a dictionary of all the meta attributes that will need to be reset on a RestoreState
1552CFDictionaryRef Transform::GetAHDictForSaveState(SecTransformStringOrAttributeRef key)
1553{
1554	SecTransformMetaAttributeType types[] =
1555	{
1556		kSecTransformMetaAttributeRequired,
1557		kSecTransformMetaAttributeRequiresOutboundConnection,
1558		kSecTransformMetaAttributeDeferred,
1559		kSecTransformMetaAttributeStream,
1560		kSecTransformMetaAttributeCanCycle,
1561		kSecTransformMetaAttributeValue
1562	};
1563
1564	CFIndex i, cnt = sizeof(types)/sizeof(SecTransformMetaAttributeType);
1565	CFTypeRef values[cnt];
1566	CFNumberRef keys[cnt];
1567	key = getAH(key);
1568
1569	// NOTE: we save meta attributes that are in their "default" state on purpose because the
1570	// default may change in the future and we definitely want to restore the default values at
1571	// time of save (i.e. if "stream=1" is the 10.7 default, but "stream=0" becomes the 10.8
1572	// default we want to load all old transforms with stream=1, the simplest way to do that is
1573	// to store all values, not just non-default values)
1574	for(i = 0; i < cnt; ++i)
1575	{
1576		values[i] = GetMetaAttribute(key, types[i]);
1577		int tmp = (int)types[i];
1578		keys[i] = CFNumberCreate(NULL, kCFNumberIntType, &tmp);
1579	}
1580
1581	static CFDictionaryValueCallBacks CFTypeOrNULL;
1582	static dispatch_once_t once;
1583	dispatch_block_t b =
1584	^{
1585		CFTypeOrNULL.version = 0;
1586		CFTypeOrNULL.retain = CFTypeOrNULLRetain;
1587		CFTypeOrNULL.release = CFTypeOrNULLRelease;
1588		CFTypeOrNULL.copyDescription = CFTypeOrNULLCopyDescription;
1589		CFTypeOrNULL.equal = CFTypeOrNULLEqual;
1590	};
1591	dispatch_once(&once, b);
1592
1593	CFDictionaryRef ret = CFDictionaryCreate(NULL, (const void**)&keys, (const void**)&values, cnt, &kCFTypeDictionaryKeyCallBacks, &CFTypeOrNULL);
1594
1595	for(i = 0; i < cnt; ++i)
1596	{
1597		CFRelease(keys[i]);
1598	}
1599
1600	return ret;
1601}
1602
1603// return everything that doesn't have ignore_while_externalizing set
1604CFDictionaryRef Transform::CopyState()
1605{
1606	CFIndex i, j, cnt = CFSetGetCount(mAttributes);
1607	transform_attribute *attrs[cnt];
1608	CFStringRef names[cnt];
1609	CFDictionaryRef values[cnt];
1610	TAGetAll(attrs);
1611	for(i = j = 0; i < cnt; ++i)
1612	{
1613		transform_attribute *ta = attrs[i];
1614		if (!ta->ignore_while_externalizing)
1615		{
1616			names[j] = ta->name;
1617			values[j++] = GetAHDictForSaveState(ta->name);
1618		}
1619	}
1620
1621	CFDictionaryRef result = CFDictionaryCreate(NULL, (const void**)&names, (const void**)&values, j, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1622
1623	for(i = j = 0; i < cnt; ++i)
1624	{
1625		transform_attribute *ta = attrs[i];
1626		if (!ta->ignore_while_externalizing)
1627		{
1628			CFRelease(values[j++]);
1629		}
1630	}
1631
1632	return result;
1633}
1634
1635
1636
1637void Transform::RestoreState(CFDictionaryRef state)
1638{
1639	CFIndex i, cnt = CFDictionaryGetCount(state);
1640	const void
1641		**keys = (const void **)alloca(sizeof(void*)*cnt),
1642		**values = (const void **)alloca(sizeof(void*)*cnt);
1643
1644	CFDictionaryGetKeysAndValues(state, keys, values);
1645
1646	// Open issue -- do we need to do anything to values that are already set, but are not in "state"?
1647	// this isn't an issue right now, which is only used on the SecTransformCopyExternalRepresentation path which starts with brand new objects,
1648	// it only becomes an issue if we add a ResetFromState, or use it internally in that role.
1649
1650	for(i = 0; i < cnt; i++)
1651	{
1652		SecTransformAttributeRef ah = getAH(keys[i]);
1653
1654		if (NULL == ah)
1655		{
1656			continue;
1657		}
1658
1659		CFIndex j, meta_cnt = CFDictionaryGetCount((CFDictionaryRef)values[i]);
1660		const void **types = (const void**)alloca(sizeof(void*)*meta_cnt), **meta_values = (const void**)alloca(sizeof(void*)*meta_cnt);
1661		CFDictionaryGetKeysAndValues((CFDictionaryRef)values[i], types, meta_values);
1662
1663		int t;
1664		for(j = 0; j < meta_cnt; ++j)
1665		{
1666			CFNumberGetValue((CFNumberRef)types[j], kCFNumberIntType, &t);
1667			if (t == kSecTransformMetaAttributeValue)
1668			{
1669				if (meta_values[j]) {
1670                    // SendMetaAttribute doesn't activate the callbacks
1671                    SetAttribute(ah, meta_values[j]);
1672                }
1673			}
1674			else
1675			{
1676				CFErrorRef result = SendMetaAttribute(ah, (SecTransformMetaAttributeType)t, meta_values[j]);
1677				if (result)
1678				{
1679					CFRelease(result); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1680				}
1681			}
1682		}
1683
1684		CFErrorRef result = SendMetaAttribute(ah, kSecTransformMetaAttributeExternalize, kCFBooleanTrue);
1685		if (result)
1686		{
1687			CFRelease(result); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1688		}
1689	}
1690}
1691
1692GroupTransform* Transform::GetRootGroup()
1693{
1694    GroupTransform *g = mGroup;
1695	if (g) {
1696        while (g->mGroup) {
1697            g = g->mGroup;
1698        }
1699    } else {
1700        if (CFGetTypeID(this->GetCFObject()) == SecGroupTransformGetTypeID()) {
1701            return (GroupTransform *)this;
1702        }
1703    }
1704    return g;
1705}
1706
1707CFDictionaryRef Transform::GetCustomExternalData()
1708{
1709	return NULL;
1710}
1711
1712void Transform::SetCustomExternalData(CFDictionaryRef customData)
1713{
1714	return;
1715}
1716
1717CFDictionaryRef Transform::Externalize(CFErrorRef* error)
1718{
1719	if (mIsActive)
1720	{
1721		return (CFDictionaryRef)CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform is executing, you need to externalize it prior to execution", GetName());
1722	}
1723
1724	// make arrays to hold the transforms and the connections
1725	__block CFMutableArrayRef transforms = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1726	__block CFMutableArrayRef connections = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1727	GroupTransform *root = GetRootGroup();
1728
1729	CFErrorRef err = ForAllNodes(false, true, ^(Transform *t) {
1730        if (t != root) {
1731            return t->ProcessExternalize(transforms, connections);
1732        }
1733        return (CFErrorRef)NULL;
1734	});
1735
1736	if (NULL != err)
1737	{
1738		// Really?  This just seems like a bad idea
1739		if (NULL != error)
1740		{
1741			*error = err;
1742		}
1743		return NULL;
1744
1745	}
1746
1747	// make a dictionary to hold the output
1748	CFMutableDictionaryRef output = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1749	CFDictionaryAddValue(output, EXTERN_TRANSFORM_TRANSFORM_ARRAY, transforms);
1750	CFDictionaryAddValue(output, EXTERN_TRANSFORM_CONNECTION_ARRAY, connections);
1751
1752	// clean up
1753	CFRelease(connections);
1754	CFRelease(transforms);
1755
1756	return output;
1757}
1758
1759CFErrorRef Transform::ProcessExternalize(CFMutableArrayRef transforms, CFMutableArrayRef connections)
1760{
1761	if (!IsExternalizable()) {
1762		return NULL;
1763	}
1764
1765	CFDictionaryRef state = CopyState();
1766	if (state && CFGetTypeID(state) == CFErrorGetTypeID()) {
1767		return (CFErrorRef)state;
1768	}
1769
1770	// make a dictionary to hold the name, type, and state of this node
1771	CFMutableDictionaryRef node = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1772	CFDictionaryAddValue(node, EXTERN_TRANSFORM_NAME, GetName());
1773
1774	CFTypeRef type = CFStringCreateCopy(NULL, mTypeName);
1775	CFDictionaryAddValue(node, EXTERN_TRANSFORM_TYPE, type);
1776	CFRelease(type);
1777
1778	if (state != NULL)
1779	{
1780		CFDictionaryAddValue(node, EXTERN_TRANSFORM_STATE, state);
1781		CFRelease(state);
1782	}
1783
1784	CFDictionaryRef customItems = GetCustomExternalData();
1785	if (NULL != customItems)
1786	{
1787		CFDictionaryAddValue(node, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY, customItems);
1788		CFRelease(customItems);
1789	}
1790
1791	// append the resulting dictionary to the node list
1792	CFArrayAppendValue(transforms, node);
1793	CFRelease(node);
1794
1795	// now walk the attribute list
1796	CFIndex numAttributes = CFSetGetCount(mAttributes);
1797	transform_attribute *attributes[numAttributes];
1798	TAGetAll(attributes);
1799
1800	CFIndex i;
1801
1802	// walk the forward links
1803	for (i = 0; i < numAttributes; ++i)
1804	{
1805		CFIndex arraySize = attributes[i]->connections ? CFArrayGetCount(attributes[i]->connections) : 0;
1806		if (arraySize != 0)
1807		{
1808			CFIndex j;
1809			for (j = 0; j < arraySize; ++j)
1810			{
1811				transform_attribute *ta = ah2ta((SecTransformAttributeRef)CFArrayGetValueAtIndex(attributes[i]->connections, j));
1812
1813				if (!ta->transform->IsExternalizable()) {
1814					// just pretend non-externalizable transforms don't even exist.   Don't write out connections, and don't talk to them about externalizing.
1815					continue;
1816				}
1817
1818				// add this forward connection to the array -- make a dictionary
1819				CFMutableDictionaryRef connection =
1820				CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1821
1822				CFDictionaryAddValue(connection, EXTERN_TRANSFORM_FROM_NAME, GetName());
1823				CFDictionaryAddValue(connection, EXTERN_TRANSFORM_FROM_ATTRIBUTE, attributes[i]->name);
1824				CFDictionaryAddValue(connection, EXTERN_TRANSFORM_TO_NAME, ta->transform->GetName());
1825				CFDictionaryAddValue(connection, EXTERN_TRANSFORM_TO_ATTRIBUTE, ta->name);
1826
1827				CFArrayAppendValue(connections, connection);
1828				CFRelease(connection);
1829			}
1830		}
1831	}
1832
1833	return NULL;
1834}
1835