1/*
2 * Copyright (c) 2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*      CFPlugIn_PlugIn.c
25        Copyright (c) 1999-2013, Apple Inc.  All rights reserved.
26        Responsibility: Tony Parker
27*/
28
29#include "CFBundle_Internal.h"
30#include "CFInternal.h"
31
32
33static void _registerFactory(const void *key, const void *val, void *context) {
34    CFStringRef factoryIDStr = (CFStringRef)key;
35    CFStringRef factoryFuncStr = (CFStringRef)val;
36    CFBundleRef bundle = (CFBundleRef)context;
37    CFUUIDRef factoryID = (CFGetTypeID(factoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(kCFAllocatorSystemDefault, factoryIDStr) : NULL;
38    if (!factoryID) factoryID = (CFUUIDRef)CFRetain(factoryIDStr);
39    if (CFGetTypeID(factoryFuncStr) != CFStringGetTypeID() || CFStringGetLength(factoryFuncStr) <= 0) factoryFuncStr = NULL;
40    CFPlugInRegisterFactoryFunctionByName(factoryID, bundle, factoryFuncStr);
41    if (factoryID) CFRelease(factoryID);
42}
43
44static void _registerType(const void *key, const void *val, void *context) {
45    CFStringRef typeIDStr = (CFStringRef)key;
46    CFArrayRef factoryIDStrArray = (CFArrayRef)val;
47    CFBundleRef bundle = (CFBundleRef)context;
48    SInt32 i, c = (CFGetTypeID(factoryIDStrArray) == CFArrayGetTypeID()) ? CFArrayGetCount(factoryIDStrArray) : 0;
49    CFStringRef curFactoryIDStr;
50    CFUUIDRef typeID = (CFGetTypeID(typeIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(kCFAllocatorSystemDefault, typeIDStr) : NULL;
51    CFUUIDRef curFactoryID;
52    if (!typeID) typeID = (CFUUIDRef)CFRetain(typeIDStr);
53    if (0 == c && CFGetTypeID(factoryIDStrArray) != CFArrayGetTypeID()) {
54        curFactoryIDStr = (CFStringRef)val;
55        curFactoryID = (CFGetTypeID(curFactoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(CFGetAllocator(bundle), curFactoryIDStr) : NULL;
56        if (!curFactoryID) curFactoryID = (CFUUIDRef)CFRetain(curFactoryIDStr);
57        CFPlugInRegisterPlugInType(curFactoryID, typeID);
58        if (curFactoryID) CFRelease(curFactoryID);
59    } else for (i = 0; i < c; i++) {
60        curFactoryIDStr = (CFStringRef)CFArrayGetValueAtIndex(factoryIDStrArray, i);
61        curFactoryID = (CFGetTypeID(curFactoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(CFGetAllocator(bundle), curFactoryIDStr) : NULL;
62        if (!curFactoryID) curFactoryID = (CFUUIDRef)CFRetain(curFactoryIDStr);
63        CFPlugInRegisterPlugInType(curFactoryID, typeID);
64        if (curFactoryID) CFRelease(curFactoryID);
65    }
66    if (typeID) CFRelease(typeID);
67}
68
69CF_PRIVATE Boolean _CFBundleNeedsInitPlugIn(CFBundleRef bundle) {
70    Boolean result = false;
71    CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle), factoryDict;
72    CFStringRef tempStr;
73    if (infoDict) {
74        factoryDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInFactoriesKey);
75        if (factoryDict && CFGetTypeID(factoryDict) == CFDictionaryGetTypeID()) result = true;
76        tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegistrationKey);
77        if (tempStr && CFGetTypeID(tempStr) == CFStringGetTypeID() && CFStringCompare(tempStr, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) result = true;
78    }
79    return result;
80}
81
82CF_PRIVATE void _CFBundleInitPlugIn(CFBundleRef bundle) {
83    CFArrayCallBacks _pluginFactoryArrayCallbacks = {0, NULL, NULL, NULL, NULL};
84    Boolean doDynamicReg = false;
85    CFDictionaryRef infoDict;
86    CFDictionaryRef factoryDict;
87    CFDictionaryRef typeDict;
88    CFStringRef tempStr;
89
90    infoDict = CFBundleGetInfoDictionary(bundle);
91    if (!infoDict) return;
92
93    factoryDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInFactoriesKey);
94    if (factoryDict && CFGetTypeID(factoryDict) != CFDictionaryGetTypeID()) factoryDict = NULL;
95    tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegistrationKey);
96    if (tempStr && CFGetTypeID(tempStr) == CFStringGetTypeID() && CFStringCompare(tempStr, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) doDynamicReg = true;
97    if (!factoryDict && !doDynamicReg) return;  // This is not a plug-in.
98
99    /* loadOnDemand is true by default if the plugIn does not do dynamic registration.  It is false, by default if it does do dynamic registration.  The dynamic register function can set this. */
100    __CFBundleGetPlugInData(bundle)->_isPlugIn = true;
101    __CFBundleGetPlugInData(bundle)->_loadOnDemand = true;
102    __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration = false;
103    __CFBundleGetPlugInData(bundle)->_instanceCount = 0;
104
105    __CFBundleGetPlugInData(bundle)->_factories = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &_pluginFactoryArrayCallbacks);
106
107    /* Now do the registration */
108
109    /* First do static registrations, if any. */
110    if (factoryDict) CFDictionaryApplyFunction(factoryDict, _registerFactory, bundle);
111    typeDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInTypesKey);
112    if (typeDict && CFGetTypeID(typeDict) != CFDictionaryGetTypeID()) typeDict = NULL;
113    if (typeDict) CFDictionaryApplyFunction(typeDict, _registerType, bundle);
114
115    /* Now set key for dynamic registration if necessary */
116    if (doDynamicReg) {
117        CFDictionarySetValue((CFMutableDictionaryRef)infoDict, CFSTR("CFPlugInNeedsDynamicRegistration"), CFSTR("YES"));
118        if (CFBundleIsExecutableLoaded(bundle)) _CFBundlePlugInLoaded(bundle);
119    }
120}
121
122CF_PRIVATE void _CFBundlePlugInLoaded(CFBundleRef bundle) {
123    CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle);
124    CFStringRef tempStr;
125    CFPlugInDynamicRegisterFunction func = NULL;
126
127    if (!__CFBundleGetPlugInData(bundle)->_isPlugIn || __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration || !infoDict || !CFBundleIsExecutableLoaded(bundle)) return;
128
129    tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, CFSTR("CFPlugInNeedsDynamicRegistration"));
130    if (tempStr && CFGetTypeID(tempStr) == CFStringGetTypeID() && CFStringCompare(tempStr, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
131        CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, CFSTR("CFPlugInNeedsDynamicRegistration"));
132        tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegisterFunctionKey);
133        if (!tempStr || CFGetTypeID(tempStr) != CFStringGetTypeID() || CFStringGetLength(tempStr) <= 0) tempStr = CFSTR("CFPlugInDynamicRegister");
134        __CFBundleGetPlugInData(bundle)->_loadOnDemand = false;
135        __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration = true;
136
137        /* Find the symbol and call it. */
138        func = (CFPlugInDynamicRegisterFunction)CFBundleGetFunctionPointerForName(bundle, tempStr);
139        if (func) {
140            func(bundle);
141            // MF:!!! Unload function is never called.  Need to deal with this!
142        }
143
144        __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration = false;
145        if (__CFBundleGetPlugInData(bundle)->_loadOnDemand && __CFBundleGetPlugInData(bundle)->_instanceCount == 0) CFBundleUnloadExecutable(bundle);   // Unload now if we can/should.
146    } else {
147        CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, CFSTR("CFPlugInNeedsDynamicRegistration"));
148    }
149}
150
151CF_PRIVATE void _CFBundleDeallocatePlugIn(CFBundleRef bundle) {
152    if (__CFBundleGetPlugInData(bundle)->_isPlugIn) {
153        SInt32 c;
154
155        /* Go through factories disabling them.  Disabling these factories should cause them to dealloc since we wouldn't be deallocating if any of the factories had outstanding instances.  So go backwards. */
156        c = CFArrayGetCount(__CFBundleGetPlugInData(bundle)->_factories);
157        while (c-- > 0) _CFPFactoryDisable((_CFPFactoryRef)CFArrayGetValueAtIndex(__CFBundleGetPlugInData(bundle)->_factories, c));
158        CFRelease(__CFBundleGetPlugInData(bundle)->_factories);
159
160        __CFBundleGetPlugInData(bundle)->_isPlugIn = false;
161    }
162}
163
164CFTypeID CFPlugInGetTypeID(void) {
165    return CFBundleGetTypeID();
166}
167
168CFPlugInRef CFPlugInCreate(CFAllocatorRef allocator, CFURLRef plugInURL) {
169    CFBundleRef bundle = CFBundleCreate(allocator, plugInURL);
170    return (CFPlugInRef)bundle;
171}
172
173CFBundleRef CFPlugInGetBundle(CFPlugInRef plugIn) {
174    return (CFBundleRef)plugIn;
175}
176
177void CFPlugInSetLoadOnDemand(CFPlugInRef plugIn, Boolean flag) {
178    if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
179        __CFBundleGetPlugInData(plugIn)->_loadOnDemand = flag;
180        if (__CFBundleGetPlugInData(plugIn)->_loadOnDemand && !__CFBundleGetPlugInData(plugIn)->_isDoingDynamicRegistration && __CFBundleGetPlugInData(plugIn)->_instanceCount == 0) {
181            /* Unload now if we can/should. */
182            /* If we are doing dynamic registration currently, do not unload.  The unloading will happen when dynamic registration is done, if necessary. */
183            CFBundleUnloadExecutable(plugIn);
184        } else if (!__CFBundleGetPlugInData(plugIn)->_loadOnDemand) {
185            /* Make sure we're loaded now. */
186            CFBundleLoadExecutable(plugIn);
187        }
188    }
189}
190
191Boolean CFPlugInIsLoadOnDemand(CFPlugInRef plugIn) {
192    if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
193        return __CFBundleGetPlugInData(plugIn)->_loadOnDemand;
194    } else {
195        return false;
196    }
197}
198
199CF_PRIVATE void _CFPlugInWillUnload(CFPlugInRef plugIn) {
200    if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
201        SInt32 c = CFArrayGetCount(__CFBundleGetPlugInData(plugIn)->_factories);
202        /* First, flush all the function pointers that may be cached by our factories. */
203        while (c-- > 0) _CFPFactoryFlushFunctionCache((_CFPFactoryRef)CFArrayGetValueAtIndex(__CFBundleGetPlugInData(plugIn)->_factories, c));
204    }
205}
206
207CF_PRIVATE void _CFPlugInAddPlugInInstance(CFPlugInRef plugIn) {
208    if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
209        if (__CFBundleGetPlugInData(plugIn)->_instanceCount == 0 && __CFBundleGetPlugInData(plugIn)->_loadOnDemand) _CFBundleUnscheduleForUnloading(CFPlugInGetBundle(plugIn));     // Make sure we are not scheduled for unloading
210        __CFBundleGetPlugInData(plugIn)->_instanceCount++;
211        /* Instances also retain the CFBundle */
212        CFRetain(plugIn);
213    }
214}
215
216CF_PRIVATE void _CFPlugInRemovePlugInInstance(CFPlugInRef plugIn) {
217    if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
218        /* MF:!!! Assert that instanceCount > 0. */
219        __CFBundleGetPlugInData(plugIn)->_instanceCount--;
220        if (__CFBundleGetPlugInData(plugIn)->_instanceCount == 0 && __CFBundleGetPlugInData(plugIn)->_loadOnDemand) {
221            // We unload the code lazily because the code that caused this function to be called is probably code from the plugin itself.  If we unload now, we will hose things.
222            //CFBundleUnloadExecutable(plugIn);
223            _CFBundleScheduleForUnloading(CFPlugInGetBundle(plugIn));
224        }
225        /* Instances also retain the CFPlugIn */
226        /* MF:!!! This will cause immediate unloading if it was the last ref on the plugin. */
227        CFRelease(plugIn);
228    }
229}
230
231CF_PRIVATE void _CFPlugInAddFactory(CFPlugInRef plugIn, _CFPFactoryRef factory) {
232    if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) CFArrayAppendValue(__CFBundleGetPlugInData(plugIn)->_factories, factory);
233}
234
235CF_PRIVATE void _CFPlugInRemoveFactory(CFPlugInRef plugIn, _CFPFactoryRef factory) {
236    if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
237        SInt32 idx = CFArrayGetFirstIndexOfValue(__CFBundleGetPlugInData(plugIn)->_factories, CFRangeMake(0, CFArrayGetCount(__CFBundleGetPlugInData(plugIn)->_factories)), factory);
238        if (idx >= 0) CFArrayRemoveValueAtIndex(__CFBundleGetPlugInData(plugIn)->_factories, idx);
239    }
240}
241