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