1#ifndef __TRANSFORM__
2#define __TRANSFORM__
3
4#include <CoreFoundation/CFError.h>
5#include "CoreFoundationBasics.h"
6#include "SecTransform.h"
7#include "SecCustomTransform.h"
8#include <dispatch/dispatch.h>
9#include "misc.h"
10
11// Since we are doing everything in CF, we just define an
12// attribute as a CFDictionary containing a value and
13// a CFArray of objects which need notification when that
14// value changes
15
16// defines for transform externalization
17#define EXTERN_TRANSFORM_TRANSFORM_ARRAY CFSTR("TRANSFORMS")
18#define EXTERN_TRANSFORM_CONNECTION_ARRAY CFSTR("ARRAY")
19#define EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY CFSTR("CUSTOM_EXPORTS")
20
21#define EXTERN_TRANSFORM_NAME CFSTR("NAME")
22#define EXTERN_TRANSFORM_TYPE CFSTR("TYPE")
23#define EXTERN_TRANSFORM_STATE CFSTR("STATE")
24#define EXTERN_TRANSFORM_FROM_NAME CFSTR("FROM_NAME")
25#define EXTERN_TRANSFORM_FROM_ATTRIBUTE CFSTR("FROM_ATTRIBUTE")
26#define EXTERN_TRANSFORM_TO_NAME CFSTR("TO_NAME")
27#define EXTERN_TRANSFORM_TO_ATTRIBUTE CFSTR("TO_ATTRIBUTE")
28
29#ifndef __clang__
30#define GCC_BUG_WORKAROUND ::
31#else
32#define GCC_BUG_WORKAROUND
33#endif
34
35
36class Monitor;
37typedef CFTypeRef SecMonitorRef;
38
39struct transform_attribute {
40	CFStringRef name;
41	CFTypeRef value;
42	CFMutableArrayRef connections;
43	// NOTE: this does NOT have a reference.
44	class Transform *transform;
45	static CFTypeID cftype;
46
47	// NOTE: NULL is a valid value to pushback, so we can't just use pushback_value==NULL for "nothing pushed back"
48	// pb_empty => no value currently pushed back
49	// pb_value => we have a value, we havn't presented it yet (if pushback_value==NULL we won't present again until another attribute changes)
50	// pb_repush => pushed back value currently being re-processed
51	// pb_presented_once => we have a value, and tried to process it and got it back again (don't present until another attribute changes)
52	//
53	enum pushback_states { pb_empty, pb_value, pb_repush, pb_presented_once, pb_discard } pushback_state;
54	CFTypeRef pushback_value;
55
56	// (for pushback support; also need pushback state & value)
57	dispatch_queue_t q;
58	dispatch_semaphore_t semaphore;
59
60	// This attribute needs a value set, or to have something connected to it before running the transform
61	unsigned int required:1;
62	// This attribute needs to have an outgoing connection before running the transform
63	unsigned int requires_outbound_connection:1;
64	// This attribute should not be presented to the transform until after execution starts
65	unsigned int deferred:1;
66	// This attribute comes in N chunks followed by a NULL
67	unsigned int stream:1;
68	// This attribute should be saved when externalizing
69	unsigned int ignore_while_externalizing:1;
70	// Set by Transform::Connect
71	unsigned int has_incoming_connection:1;
72	// CustomTransform should't special case CFErrors for this attribute
73	unsigned int direct_error_handling:1;
74	// External sets are problematic, I think they should be disallowed full stop, but 7947393 says we need them sometimes
75	unsigned int allow_external_sets:1;
76	// Value has been created as a source (therefore deferred), give it special treatment
77	unsigned int has_been_deferred:1;
78
79	void *attribute_changed_block;
80	void *attribute_validate_block;
81};
82
83typedef void (^ActivityMonitor)(CFStringRef name, CFTypeRef value);
84
85class GroupTransform; 	// Forward reference so we do not have to include
86						// the header and break a circular dependency
87class BlockMonitor;
88
89class Transform : public CoreFoundationObject
90{
91	friend CFTypeRef SecTransformExecute(SecTransformRef tranformRef, CFErrorRef* errorRef);
92	friend CFTypeRef SecTransformGetAttribute(SecTransformRef transformRef, CFStringRef key);
93	friend class BlockMonitor;
94protected:
95	dispatch_queue_t mDispatchQueue, mActivationQueue;
96	dispatch_group_t mActivationPending;
97	CFMutableSetRef mAttributes;
98	CFMutableArrayRef mPushedback;
99	Boolean mIsActive;
100	Boolean mIsFinalizing;
101	Boolean mAlwaysSelfNotify, mProcessingPushbacks;
102	GroupTransform *mGroup;
103	CFErrorRef mAbortError;
104	CFStringRef mTypeName;
105
106	SecTransformAttributeRef AbortAH, DebugAH;
107
108	Transform(CFStringRef transformType, CFStringRef CFobjectType = CFSTR("SecTransform"));
109
110	transform_attribute *getTA(SecTransformStringOrAttributeRef attr, bool create_ok);
111	void TAGetAll(transform_attribute **attributes);
112	CFIndex GetAttributeCount();
113
114	CFDictionaryRef GetAHDictForSaveState(SecTransformStringOrAttributeRef key);
115
116	CFTypeRef ValueForNewAttribute(CFStringRef key, CFTypeRef value);
117	CFMutableDictionaryRef AddNewAttribute(CFStringRef key, CFTypeRef value);
118	CFErrorRef SetAttributeNoCallback(SecTransformStringOrAttributeRef key, CFTypeRef value);
119
120	CFErrorRef ProcessExecute(CFStringRef &outputAttached, SecMonitorRef monitor);
121	typedef void (^AccumulateDictonary)(CFDictionaryRef d);
122	CFErrorRef ProcessExternalize(CFMutableArrayRef transforms, CFMutableArrayRef connections);
123
124	void FinalizeForClang();
125
126	virtual void Finalize();
127	// subclasses with non-trivial finalization can implement this (default: delete this)
128	virtual void FinalizePhase2();
129    // subclasses that want to reject some connections can use this
130    virtual bool validConnectionPoint(CFStringRef attributeName);
131
132	void try_pushbacks();
133
134	void Initialize();
135
136	void ActivateInputs();
137
138	virtual std::string DebugDescription();
139
140	typedef CFErrorRef (^TransformOperation)(Transform*);
141	typedef void (^TransformAsyncOperation)(Transform*);
142    CFErrorRef ForAllNodes(bool parallel, bool includeOwningGroup, TransformOperation op);
143
144	CFErrorRef TraverseTransform(CFMutableSetRef visited, TransformOperation t);
145
146
147	CFErrorRef SendAttribute(SecTransformStringOrAttributeRef key, CFTypeRef value);
148	CFErrorRef SendMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type, CFTypeRef value);
149
150    // Abort all transforms in this transform's RootGroup, including this transform
151	virtual void AbortAllTransforms(CFTypeRef error);
152    // Abort just this transform (and maybe schedule a later call to AbortAllTransforms), should only be
153    // called via AbortAllTransforms
154	virtual void AbortJustThisTransform(CFErrorRef abortMsg);
155
156	void phase3Activation();
157    void DoPhase3Activation();
158
159	bool HasNoInboundConnections();
160	bool HasNoOutboundConnections();
161
162private:
163	CFErrorRef ExecuteOperation(CFStringRef &outputAttached, SecMonitorRef output, dispatch_queue_t phase2, dispatch_queue_t phase3);
164	SecTransformAttributeRef makeAH(transform_attribute *ta);
165
166public:
167
168	static CFTypeID GetCFTypeID();
169
170	// these functions are overloaded to implement the functionality of your transform
171	virtual ~Transform();
172
173	// this is called when one of your attributes (e.g. input) changes
174	virtual void AttributeChanged(SecTransformAttributeRef ah, CFTypeRef value);
175	// this is for backwards compatibility only (XXX: convert all existing Transform subclasses to not use it then remove)
176	virtual void AttributeChanged(CFStringRef name, CFTypeRef value);
177
178	// overload to return true if your transform can be externalized (generally true unless you are a monitor)
179	virtual bool IsExternalizable();
180
181	// Base implementation saves all attributes that have kSecTransformMetaAttributeExternalize TRUE (which is the default).
182	// If that isn't useful for your transform overload to return a CFDictionary that contains the state of
183	// your transform.  Values returned should be serializable.  Remember that this state will be restored
184	// before SecTransformExecute is called.  Do not include the transform name in your state (this will be
185	// done for you by SecTransformCopyExternalRep).
186	virtual CFDictionaryRef CopyState();
187
188	// overload to restore the state of your transform
189	virtual void RestoreState(CFDictionaryRef state);
190	virtual void SetCustomExternalData(CFDictionaryRef customData);
191
192	virtual Boolean TransformCanExecute();
193	virtual CFErrorRef TransformStartingExecution();
194
195	SecTransformAttributeRef getAH(SecTransformStringOrAttributeRef attr, bool create_ok =true, bool create_undesrscore_ok =false);
196	CFArrayRef GetAllAH();
197
198	CFStringRef GetName();
199
200	// Output debugging information if the DEBUG attribute is set for this transform
201	void Debug(const char *fmt, ...);
202
203	CFErrorRef RefactorErrorToIncludeAbortingTransform(CFErrorRef sourceError);
204
205public:
206	CFErrorRef Connect(GroupTransform *group, Transform* destinationTransform, CFStringRef myKey, CFStringRef hisKey);
207	CFErrorRef Disconnect(Transform* destinationTransform, CFStringRef myKey, CFStringRef hisKey);
208
209	CFErrorRef ExternalSetAttribute(SecTransformStringOrAttributeRef key, CFTypeRef value);
210	CFErrorRef SetAttribute(SecTransformStringOrAttributeRef key, CFTypeRef value);
211	CFTypeRef GetAttribute(SecTransformStringOrAttributeRef key);
212	CFTypeRef GetMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type);
213
214	CFErrorRef Pushback(SecTransformAttributeRef ah, CFTypeRef value);
215
216	void Do(SecTransformAttributeRef name, CFTypeRef value);
217
218	CFTypeRef Execute(dispatch_queue_t deliveryQueue, SecMessageBlock deliveryBlock, CFErrorRef* errorRef);
219
220	// set to get notified every time this transform does something -- used for debugging
221	void SetActivityMonitor(ActivityMonitor am);
222
223	virtual CFDictionaryRef Externalize(CFErrorRef *error);
224
225    // Returns NULL if not in a group; can return this
226    GroupTransform* GetRootGroup();
227
228	friend class GroupTransform;
229	friend Transform::TransformOperation makeIdleOp(dispatch_group_t idle_group);
230
231	void SetGroup(GroupTransform* group) {mGroup = group;}
232	CFDictionaryRef GetCustomExternalData();
233};
234
235
236inline struct transform_attribute *ah2ta(SecTransformAttributeRef ah) {
237	// CF stores our data just after the CFRuntimeBase, we just have a single pointer there.
238	return *(struct transform_attribute **)(1 + (CFRuntimeBase*)ah);
239}
240
241#endif
242