/* * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /*! * @header CPlugInList */ #include "CPlugInList.h" #include "CServerPlugin.h" #include "CLauncher.h" #include "DSUtils.h" #include "PrivateTypes.h" #include "SharedConsts.h" #include "CPluginConfig.h" #include "CLog.h" #include "CNodeList.h" #include "DSLDAPUtils.h" #include #include #include #include extern CFRunLoopRef gPluginRunLoop; extern DSMutexSemaphore *gKerberosMutex; extern CPluginConfig *gPluginConfig; extern DSMutexSemaphore *gLazyPluginLoadingLock; extern CPlugInList *gPlugins; extern CNodeList *gNodeList; UInt32 gMaxLazyLoaders = 20; char* gLazyLoadAttemptedList[20] = {nil}; //Use of the table entries directly will need a Mutex if the order can change //but likely will continue only to add on to the end of the table and //not delete table entries so it is currently a non issue // --------------------------------------------------------------------------- // * CPlugInList () // // --------------------------------------------------------------------------- CPlugInList::CPlugInList ( void ) : fMutex("CPlugInList::fMutex") { fPICount = 0; fTable = nil; fTableTail = nil; fCFRecordTypeRestrictions = NULL; } // CPlugInList // --------------------------------------------------------------------------- // * ~CPlugInList () // // --------------------------------------------------------------------------- CPlugInList::~CPlugInList ( void ) { } // ~CPlugInList // --------------------------------------------------------------------------- // * AddPlugIn () // // --------------------------------------------------------------------------- SInt32 CPlugInList::AddPlugIn ( const char *inName, const char *inVersion, const char *inConfigAvail, const char *inConfigFile, eDSPluginLevel inLevel, FourCharCode inKey, CServerPlugin *inPluginPtr, CFPlugInRef inPluginRef, CFUUIDRef inCFuuidFactory, UInt32 inULVers ) { SInt32 siResult = eDSInvalidPlugInConfigData; sTableData *aTableEntry = nil; if ( inName == nil ) { return( eDSNullParameter ); } // ask the plugin about it state outside the lock to avoid deadlock. ePluginState pluginState = gPluginConfig->GetPluginState( inName ); fMutex.WaitLock(); try { aTableEntry = (sTableData *)calloc(1, sizeof(sTableData)); if (fTableTail != nil) { fTableTail->pNext = aTableEntry; fTableTail = aTableEntry; } else { fTable = aTableEntry; fTableTail = aTableEntry; } fTableTail->pNext = nil; fTableTail->fName = inName; fTableTail->fVersion = inVersion; fTableTail->fConfigAvail = inConfigAvail; fTableTail->fConfigFile = inConfigFile; fTableTail->fPluginPtr = inPluginPtr; fTableTail->fValidDataStamp = 0; fTableTail->fLevel = inLevel; if ( inPluginRef ) { fTableTail->fPluginRef = inPluginRef; CFRetain( fTableTail->fPluginRef ); } if ( inCFuuidFactory ) { fTableTail->fCFuuidFactory = inCFuuidFactory; CFRetain( fTableTail->fCFuuidFactory ); } if ( inULVers ) fTableTail->fULVers = inULVers; fTableTail->fKey = inKey; fTableTail->fState = pluginState | kUninitialized; fPICount++; siResult = eDSNoErr; } catch( SInt32 err ) { siResult = err; } fMutex.SignalLock(); return( siResult ); } // AddPlugIn void CPlugInList::LoadPlugin( sTableData *inTableEntry ) { bool done = false; SInt32 siResult = eDSNoErr; UInt32 uiCntr = 0; UInt32 uiAttempts = 100; UInt32 uiWaitTime = 1; sHeader aHeader; CServerPlugin *ourPluginPtr= nil; UInt32 listCount = 0; gLazyPluginLoadingLock->WaitLock(); // check the list of already attempted lazy loads before continuing ie. eliminate race possibility while( (gLazyLoadAttemptedList[listCount] != nil) && (listCount < gMaxLazyLoaders) ) { if (strcmp(gLazyLoadAttemptedList[listCount],inTableEntry->fName) == 0) { gLazyPluginLoadingLock->SignalLock(); return; } listCount++; } if (listCount != gMaxLazyLoaders) { // add to the list since we attempt to load this one gLazyLoadAttemptedList[listCount] = strdup(inTableEntry->fName); } try { if ( inTableEntry->fPluginPtr == nil) { inTableEntry->fPluginPtr = new CServerPlugin( inTableEntry->fPluginRef, inTableEntry->fCFuuidFactory, inTableEntry->fKey, inTableEntry->fULVers, inTableEntry->fName ); ourPluginPtr = (CServerPlugin *)inTableEntry->fPluginPtr; if ( ourPluginPtr == NULL ) throw( (SInt32)eMemoryError ); ourPluginPtr->Validate( inTableEntry->fVersion, inTableEntry->fKey ); while ( !done ) { uiCntr++; // Attempt to initialize it siResult = ourPluginPtr->Initialize(); if ( ( siResult != eDSNoErr ) && ( uiCntr == 1 ) ) { ErrLog( kLogApplication, "Attempt #%l to initialize plug-in %s failed.\n Will retry initialization at most 100 times every %l second.", uiCntr, ourPluginPtr->GetPluginName(), uiWaitTime ); } if ( siResult == eDSNoErr ) { DbgLog( kLogApplication, "Initialization of plug-in %s succeeded with #%l attempt.", ourPluginPtr->GetPluginName(), uiCntr ); // we start initialized but inactive, we set our active flag later inTableEntry->fState = kInitialized | kInactive; //provide the CFRunLoop to the plugins that need it if (gPluginRunLoop != NULL) { aHeader.fType = kServerRunLoop; aHeader.fResult = eDSNoErr; aHeader.fContextData = (void *)gPluginRunLoop; siResult = ourPluginPtr->ProcessRequest( (void*)&aHeader ); //don't handle return } // provide the Kerberos Mutex to plugins that need it if (gKerberosMutex != NULL) { aHeader.fType = kKerberosMutex; aHeader.fResult = eDSNoErr; aHeader.fContextData = (void *)gKerberosMutex; ourPluginPtr->ProcessRequest( (void*)&aHeader ); // don't handle return } done = true; } if ( !done ) { // We will try this 100 times before we bail if ( uiCntr == uiAttempts ) { ErrLog( kLogApplication, "%l attempts to initialize plug-in %s failed.\n Setting plug-in state to inactive.", uiCntr, ourPluginPtr->GetPluginName() ); inTableEntry->fState = kInactive | kFailedToInit; done = true; } else { fWaitToInit.WaitForEvent( uiWaitTime * kMilliSecsPerSec ); } } } SrvrLog( kLogApplication, "Plugin \"%s\", Version \"%s\", loaded on demand successfully.", inTableEntry->fName, inTableEntry->fVersion ); } } catch( SInt32 err ) { SrvrLog( kLogApplication, "Plugin \"%s\", Version \"%s\", failed to load on demand (%d).", inTableEntry->fName, inTableEntry->fVersion, err ); } gLazyPluginLoadingLock->SignalLock(); } // --------------------------------------------------------------------------- // * InitPlugIns () // // --------------------------------------------------------------------------- void CPlugInList::InitPlugIns ( eDSPluginLevel inLevel ) { sTableData *aTableEntry = nil; fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if ( ( aTableEntry->fLevel == inLevel) && ( ( aTableEntry->fState & kUninitialized ) || ( aTableEntry->fState == 0 ) ) ) { if ( (aTableEntry->fName != nil) && (aTableEntry->fPluginPtr != nil) ) { try { //this constructor could throw CLauncher *cpLaunch = new CLauncher( (CServerPlugin *)aTableEntry->fPluginPtr ); if ( cpLaunch != nil ) { //this call could throw cpLaunch->StartThread(); } DbgLog( kLogApplication, "Plugin \"%s\", Version \"%s\", activated successfully.", aTableEntry->fName, aTableEntry->fVersion ); } catch( SInt32 err ) { DbgLog( kLogApplication, "Plugin \"%s\", Version \"%s\", failed to launch initialization thread.", aTableEntry->fName, aTableEntry->fVersion ); } } else if ( aTableEntry->fName != nil ) { // if this plugin is supposed to be active, we should mark it as such, it still should be uninitialized. ePluginState pluginState = gPluginConfig->GetPluginState( aTableEntry->fName ); aTableEntry->fState = pluginState | kUninitialized; DbgLog( kLogApplication, "Plugin \"%s\", Version \"%s\", referenced to be loaded on demand successfully.", aTableEntry->fName, aTableEntry->fVersion ); } } aTableEntry = aTableEntry->pNext; } fMutex.SignalLock(); } // InitPlugIns // --------------------------------------------------------------------------- // * IsPresent () // // --------------------------------------------------------------------------- SInt32 CPlugInList::IsPresent ( const char *inName ) { SInt32 siResult = ePluginNameNotFound; sTableData *aTableEntry = nil; if ( inName == nil ) { return( eDSNullParameter ); } fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if ( aTableEntry->fName != nil ) { if ( ::strcmp( aTableEntry->fName, inName ) == 0 ) { siResult = eDSNoErr; break; } } aTableEntry = aTableEntry->pNext; } fMutex.SignalLock(); return( siResult ); } // IsPresent // --------------------------------------------------------------------------- // * SetState () // // --------------------------------------------------------------------------- SInt32 CPlugInList::SetState ( const char *inName, const UInt32 inState ) { SInt32 siResult = ePluginNameNotFound; sTableData *aTableEntry = nil; sTableData *tmpTableEntry = nil; UInt32 curState = kUnknownState; sTableData *pluginEntry = NULL; if ( inName == nil ) { return( eDSNullParameter ); } fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if ( aTableEntry->fName != nil ) { if ( ::strcmp( aTableEntry->fName, inName ) == 0 ) { curState = aTableEntry->fState; // this means we will try to load the plugin below if ( (inState & kActive) && aTableEntry->fPluginPtr == NULL ) tmpTableEntry = MakeTableEntryCopy( aTableEntry ); pluginEntry = aTableEntry; break; } } aTableEntry = aTableEntry->pNext; } fMutex.SignalLock(); if (tmpTableEntry != nil) { // This plugin has just been set to active but we haven't loaded it yet. // should NOT hold the table mutex for this gNodeList->Lock(); LoadPlugin( tmpTableEntry ); gNodeList->Unlock(); if (tmpTableEntry->fPluginPtr != NULL) { // we actually loaded the plugin so go ahead and update the table fMutex.WaitLock(); // now use the tmpTableEntry pluginPtr if ( pluginEntry->fPluginPtr == NULL ) pluginEntry->fPluginPtr = tmpTableEntry->fPluginPtr; fMutex.SignalLock(); } DSFree(tmpTableEntry); } // so we can call SetPluginState without the mutex being locked to avoid deadlock. CServerPlugin* pluginPtr = NULL; fMutex.WaitLock(); if ( (curState & inState) != inState && pluginEntry != NULL && pluginEntry->fPluginPtr != NULL ) { pluginEntry->fState = inState; // this will trigger a call to SetPluginState below pluginPtr = pluginEntry->fPluginPtr; siResult = eDSNoErr; } fMutex.SignalLock(); if ( pluginPtr ) { pluginPtr->SetPluginState( inState ); } return( siResult ); } // SetState // --------------------------------------------------------------------------- // * GetState () // // --------------------------------------------------------------------------- SInt32 CPlugInList::GetState ( const char *inName, UInt32 *outState ) { SInt32 siResult = ePluginNameNotFound; sTableData *aTableEntry = nil; if ( inName == nil ) { return( eDSNullParameter ); } fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if ( aTableEntry->fName != nil ) { if ( ::strcmp( aTableEntry->fName, inName ) == 0 ) { *outState = aTableEntry->fState; siResult = eDSNoErr; break; } } aTableEntry = aTableEntry->pNext; } fMutex.SignalLock(); return( siResult ); } // GetState // --------------------------------------------------------------------------- // * UpdateValidDataStamp () // // --------------------------------------------------------------------------- SInt32 CPlugInList::UpdateValidDataStamp ( const char *inName ) { SInt32 siResult = ePluginNameNotFound; sTableData *aTableEntry = nil; if ( inName == nil ) { return( eDSNullParameter ); } fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if ( aTableEntry->fName != nil ) { if ( ::strcmp( aTableEntry->fName, inName ) == 0 ) { aTableEntry->fValidDataStamp++; siResult = eDSNoErr; break; } } aTableEntry = aTableEntry->pNext; } fMutex.SignalLock(); return( siResult ); } // UpdateValidDataStamp // --------------------------------------------------------------------------- // * GetValidDataStamp () // // --------------------------------------------------------------------------- UInt32 CPlugInList::GetValidDataStamp ( const char *inName ) { UInt32 outStamp = 0; sTableData *aTableEntry = nil; if ( inName == nil ) { return( eDSNullParameter ); } fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if ( aTableEntry->fName != nil ) { if ( ::strcmp( aTableEntry->fName, inName ) == 0 ) { outStamp = aTableEntry->fValidDataStamp; break; } } aTableEntry = aTableEntry->pNext; } fMutex.SignalLock(); return( outStamp ); } // GetValidDataStamp // --------------------------------------------------------------------------- // * GetPlugInCount () // // --------------------------------------------------------------------------- UInt32 CPlugInList::GetPlugInCount ( void ) { return( fPICount ); } // GetPlugInCount // --------------------------------------------------------------------------- // * GetActiveCount () // // --------------------------------------------------------------------------- UInt32 CPlugInList::GetActiveCount ( void ) { UInt32 activeCount = 0; sTableData *aTableEntry = nil; fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if ( aTableEntry->fName == nil ) { if ( aTableEntry->fState & kActive ) { activeCount++; } } aTableEntry = aTableEntry->pNext; } fMutex.SignalLock(); return( activeCount ); } // GetActiveCount // --------------------------------------------------------------------------- // * SetPluginState () // // --------------------------------------------------------------------------- void CPlugInList::SetPluginState( CServerPlugin *inPluginPtr, ePluginState inPluginState ) { if ( inPluginPtr == NULL ) return; SInt32 siResult = inPluginPtr->SetPluginState( inPluginState ); if ( siResult == eDSNoErr ) SrvrLog( kLogApplication, "Plug-in %s state is now %s.", inPluginPtr->GetPluginName(), (inPluginState == kActive ? "active" : "inactive") ); else SrvrLog( kLogApplication, "Unable to set %s plug-in state to %s. Received error %l.", inPluginPtr->GetPluginName(), (inPluginState == kActive ? "active" : "inactive"), siResult ); } // --------------------------------------------------------------------------- // * GetPlugInPtr () // // --------------------------------------------------------------------------- CServerPlugin* CPlugInList::GetPlugInPtr ( const char *inName, bool loadIfNeeded ) { CServerPlugin *pResult = nil; sTableData *aTableEntry = nil; sTableData *tmpTableEntry = nil; ePluginState newState = kUnknownState; if ( inName == nil ) { return( nil ); } fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if ( (aTableEntry->fName != nil) ) { if ( ::strcmp( aTableEntry->fName, inName ) == 0 ) { // if someone specifically asks for a node, even if not active, we should load the plugin // this can be for configure nodes, otherwise inactive nodes cannot be configured if ( (aTableEntry->fPluginPtr == NULL) && loadIfNeeded ) { // this means we will try to load the plugin below tmpTableEntry = MakeTableEntryCopy( aTableEntry ); break; } pResult = aTableEntry->fPluginPtr; break; } } aTableEntry = aTableEntry->pNext; } fMutex.SignalLock(); if (tmpTableEntry != nil) { // This plugin has just been set to active but we haven't loaded it yet. // should NOT hold the table mutex for this gNodeList->Lock(); LoadPlugin( tmpTableEntry ); gNodeList->Unlock(); // we actually loaded the plugin so go ahead and update the table fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if ( aTableEntry->fName != nil ) { if ( ::strcmp( aTableEntry->fName, inName ) == 0 ) { if ( loadIfNeeded && aTableEntry->fPluginPtr == NULL ) { // now use the tmpTableEntry aTableEntry->fPluginPtr = tmpTableEntry->fPluginPtr; aTableEntry->fState = tmpTableEntry->fState; if ( aTableEntry->fPluginPtr != NULL ) { // save in newState so that we can call out to the plugin ouside the lock. newState = gPluginConfig->GetPluginState( aTableEntry->fPluginPtr->GetPluginName() ); aTableEntry->fState = newState; } } pResult = aTableEntry->fPluginPtr; break; } } aTableEntry = aTableEntry->pNext; } fMutex.SignalLock(); DSFree(tmpTableEntry); // if the plugin was loaded, set the state. needs to be done // with the mutex unlocked to prevent deadlock. if ( loadIfNeeded && pResult != NULL ) { SetPluginState( pResult, newState ); } } return( pResult ); } // GetPlugInPtr // --------------------------------------------------------------------------- // * GetPlugInPtr () // // --------------------------------------------------------------------------- CServerPlugin* CPlugInList::GetPlugInPtr ( const UInt32 inKey, bool loadIfNeeded ) { CServerPlugin *pResult = nil; sTableData *aTableEntry = nil; sTableData *tmpTableEntry = nil; ePluginState newState = kUnknownState; fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if ( (aTableEntry->fName != nil) ) { if ( aTableEntry->fKey == inKey ) { if ( aTableEntry->fPluginPtr == NULL && (gPluginConfig->GetPluginState(aTableEntry->fName) & kActive) && loadIfNeeded ) { // this means we will try to load the plugin below tmpTableEntry = MakeTableEntryCopy( aTableEntry ); break; } pResult = aTableEntry->fPluginPtr; break; } } aTableEntry = aTableEntry->pNext; } fMutex.SignalLock(); if (tmpTableEntry != nil) { // This plugin has just been set to active but we haven't loaded it yet. // should NOT hold the table mutex for this gNodeList->Lock(); LoadPlugin( tmpTableEntry ); gNodeList->Unlock(); // we actually loaded the plugin so go ahead and update the table fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if ( aTableEntry->fName != nil ) { if ( aTableEntry->fKey == inKey ) { if ( aTableEntry->fPluginPtr == NULL && (gPluginConfig->GetPluginState(aTableEntry->fName) & kActive) && loadIfNeeded ) { // now use the tmpTableEntry aTableEntry->fPluginPtr = tmpTableEntry->fPluginPtr; aTableEntry->fState = tmpTableEntry->fState; if ( aTableEntry->fPluginPtr != NULL ) { // save in newState so that we can call out to the plugin ouside the lock. newState = gPluginConfig->GetPluginState( aTableEntry->fPluginPtr->GetPluginName() ); aTableEntry->fState = newState; } } pResult = aTableEntry->fPluginPtr; break; } } aTableEntry = aTableEntry->pNext; } fMutex.SignalLock(); DSFree(tmpTableEntry); // if the plugin was loaded, set the state. needs to be done // with the mutex unlocked to prevent deadlock. if ( loadIfNeeded && pResult != NULL ) { SetPluginState( pResult, newState ); } } return( pResult ); } // GetPlugInPtr // --------------------------------------------------------------------------- // * Next () // // --------------------------------------------------------------------------- CServerPlugin* CPlugInList::Next ( UInt32 *inIndex ) { CServerPlugin *pResult = nil; UInt32 tableIndex = 0; sTableData *aTableEntry = nil; fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if (tableIndex == *inIndex) { if ( (aTableEntry->fName != nil) && (aTableEntry->fPluginPtr != nil) ) { pResult = aTableEntry->fPluginPtr; tableIndex++; break; } else { *inIndex = tableIndex + 1; // keep looking for next loaded plugin } } tableIndex++; aTableEntry = aTableEntry->pNext; } *inIndex = tableIndex; fMutex.SignalLock(); return( pResult ); } // Next // --------------------------------------------------------------------------- // * GetPlugInInfo () // // --------------------------------------------------------------------------- CPlugInList::sTableData* CPlugInList::GetPlugInInfo ( UInt32 inIndex ) { UInt32 tableIndex = 0; sTableData *aTableEntry = nil; fMutex.WaitLock(); aTableEntry = fTable; while ( aTableEntry != nil ) { if (tableIndex == inIndex) { break; } tableIndex++; aTableEntry = aTableEntry->pNext; } fMutex.SignalLock(); return( aTableEntry ); } // GetPlugInInfo // --------------------------------------------------------------------------- // * CopyRecordTypeRestrictionsDictionary( void ) // // --------------------------------------------------------------------------- CFMutableDictionaryRef CPlugInList::CopyRecordTypeRestrictionsDictionary( void ) { CFMutableDictionaryRef restrictions = NULL; fMutex.WaitLock(); if( fCFRecordTypeRestrictions ) restrictions = CFDictionaryCreateMutableCopy( kCFAllocatorDefault, NULL, fCFRecordTypeRestrictions ); else restrictions = CFDictionaryCreateMutable( kCFAllocatorDefault, NULL, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); fMutex.SignalLock(); return(restrictions); } // CopyRecordTypeRestrictionsDictionary // --------------------------------------------------------------------------- // * SetRecordTypeRestrictionsDictionary( CFMutableDictionaryRef inDictionary ) // // --------------------------------------------------------------------------- void CPlugInList::SetRecordTypeRestrictionsDictionary( CFMutableDictionaryRef inDictionary ) { CFURLRef configFileURL = NULL; CFDataRef xmlData = NULL; CFStringRef sPath = NULL; SInt32 errorCode = 0; SInt32 siResult = 0; if (inDictionary != NULL) { fMutex.WaitLock(); DSCFRelease(fCFRecordTypeRestrictions); CFRetain( inDictionary ); fCFRecordTypeRestrictions = inDictionary; sPath = CFStringCreateWithCString( kCFAllocatorDefault, kRecTypeRestrictionsFilePath, kCFStringEncodingUTF8 ); if (sPath != NULL) { configFileURL = ::CFURLCreateWithFileSystemPath( kCFAllocatorDefault, sPath, kCFURLPOSIXPathStyle, false ); DSCFRelease( sPath ); xmlData = CFPropertyListCreateXMLData(NULL, inDictionary); if ( (configFileURL != NULL) && (xmlData != NULL) ) { //write the XML to the config file siResult = CFURLWriteDataAndPropertiesToResource( configFileURL, xmlData, NULL, &errorCode); if (siResult == eDSNoErr) { chmod( kRecTypeRestrictionsFilePath, S_IRUSR | S_IWUSR ); } } DSCFRelease(configFileURL); DSCFRelease(xmlData); } fMutex.SignalLock(); } } // SetRecordTypeRestrictionsDictionary // --------------------------------------------------------------------------- // * ReadRecordTypeRestrictions () // // --------------------------------------------------------------------------- SInt32 CPlugInList::ReadRecordTypeRestrictions( void ) { SInt32 siResult = eDSNoErr; CFURLRef configFileURL = NULL; CFURLRef configFileCorruptedURL = NULL; CFDataRef xmlData = NULL; struct stat statResult; bool bReadFile = false; bool bCorruptedFile = false; bool bWroteFile = false; SInt32 errorCode = 0; CFStringRef sCorruptedPath = NULL; CFStringRef sPath = NULL; fMutex.WaitLock(); //Config data is read from a plist file //Steps in the process: //1- see if the file exists //2- if it exists then try to read it //3- if existing file is corrupted then rename it and save it while creating a new default file //4- if file doesn't exist then create a new default file - make sure directories exist/if not create them //step 1- see if the file exists //if not then make sure the directories exist or create them //then write the file siResult = ::stat( kRecTypeRestrictionsFilePath, &statResult ); sPath = CFStringCreateWithCString( kCFAllocatorDefault, kRecTypeRestrictionsFilePath, kCFStringEncodingUTF8 ); if (sPath != NULL) { //create URL always configFileURL = ::CFURLCreateWithFileSystemPath( kCFAllocatorDefault, sPath, kCFURLPOSIXPathStyle, false ); DSCFRelease( sPath ); //if file does not exist, let's make sure the directories are there if (siResult != eDSNoErr) { // file does not exist so checking directory path to enable write of a new file CreatePrefDirectory(); //create XML data from the default config UInt32 uiDataSize = ::strlen( kDefaultRecTypeRestrictionsConfig ); xmlData = ::CFDataCreate( nil, (const UInt8 *)kDefaultRecTypeRestrictionsConfig, uiDataSize ); DbgLog( kLogPlugin, "CPlugInList: Created a new Record Type Restrictions config file since it did not exist" ); if ( (configFileURL != NULL) && (xmlData != NULL) ) { //write the XML to the config file siResult = CFURLWriteDataAndPropertiesToResource( configFileURL, xmlData, NULL, &errorCode); } DSCFRelease(xmlData); } // file does not exist so creating one if ( (siResult == eDSNoErr) && (configFileURL != NULL) ) //either stat or new write was successful { chmod( kRecTypeRestrictionsFilePath, S_IRUSR | S_IWUSR ); // Read the XML property list file bReadFile = CFURLCreateDataAndPropertiesFromResource( kCFAllocatorDefault, configFileURL, &xmlData, // place to put file data NULL, NULL, &siResult); } } // if (sPath != NULL) if (bReadFile) { CFPropertyListRef configPropertyList = NULL; if (xmlData != nil) { // extract the config dictionary from the XML data. configPropertyList = CFPropertyListCreateFromXMLData( kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL); if (configPropertyList != nil ) { //make the propertylist a dict if ( CFDictionaryGetTypeID() == CFGetTypeID( configPropertyList ) ) { DSCFRelease(fCFRecordTypeRestrictions); fCFRecordTypeRestrictions = (CFDictionaryRef) configPropertyList; } } } //check if this XML blob is a property list and can be made into a dictionary if (fCFRecordTypeRestrictions == NULL) { //if it is not then say the file is corrupted and save off the corrupted file DbgLog( kLogPlugin, "CPlugInList: Record Type Restrictions config file is corrupted" ); bCorruptedFile = true; //here we need to make a backup of the file - why? - because // Append the subpath. sCorruptedPath = ::CFStringCreateWithCString( kCFAllocatorDefault, kRecTypeRestrictionsCorruptedFilePath, kCFStringEncodingUTF8 ); if (sCorruptedPath != NULL) { // Convert it into a CFURL. configFileCorruptedURL = ::CFURLCreateWithFileSystemPath( kCFAllocatorDefault, sCorruptedPath, kCFURLPOSIXPathStyle, false ); DSCFRelease( sCorruptedPath ); // build with Create so okay to dealloac here if (configFileCorruptedURL != NULL) { //write the XML to the corrupted copy of the config file bWroteFile = CFURLWriteDataAndPropertiesToResource( configFileCorruptedURL, xmlData, NULL, &errorCode); if (bWroteFile) { chmod( kRecTypeRestrictionsCorruptedFilePath, S_IRUSR | S_IWUSR ); } } } } DSCFRelease(xmlData); } else //existing file is unreadable { DbgLog( kLogPlugin, "CPlugInList: Record Type Restrictions config file is unreadable" ); bCorruptedFile = true; } if (bCorruptedFile) { //create XML data from the default config UInt32 uiDataSize = ::strlen( kDefaultRecTypeRestrictionsConfig ); xmlData = ::CFDataCreate( nil, (const UInt8 *)kDefaultRecTypeRestrictionsConfig, uiDataSize ); DbgLog( kLogPlugin, "CPlugInList: Created a new Record Type Restrictions config file since existing one was corrupted" ); DSCFRelease(fCFRecordTypeRestrictions); //assume that the XML blob is good since we created it here fCFRecordTypeRestrictions = (CFDictionaryRef)CFPropertyListCreateFromXMLData( kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL); if ( (configFileURL != NULL) && (xmlData != NULL) ) { //write the XML to the config file siResult = CFURLWriteDataAndPropertiesToResource( configFileURL, xmlData, NULL, &errorCode); if (siResult == eDSNoErr) { chmod( kRecTypeRestrictionsFilePath, S_IRUSR | S_IWUSR ); } } DSCFRelease(xmlData); } DSCFRelease(configFileURL); // seems okay to dealloc since Create used and done with it now DSCFRelease(configFileCorruptedURL); // seems okay to dealloc since Create used and done with it now fMutex.SignalLock(); return( siResult ); } // ReadRecordTypeRestrictions // --------------------------------------------------------------------------- // * CreatePrefDirectory // --------------------------------------------------------------------------- bool CPlugInList::CreatePrefDirectory( void ) { int siResult = eDSNoErr; struct stat statResult; DbgLog( kLogPlugin, "CPlugInList: Checking for Record Type Restrictions config file:" ); DbgLog( kLogPlugin, "CPlugInList: %s", kRecTypeRestrictionsFilePath ); //step 1- see if the file exists //if not then make sure the directories exist or create them //then create a new file if necessary siResult = ::stat( kRecTypeRestrictionsFilePath, &statResult ); //if file does not exist if (siResult != eDSNoErr) siResult = dsCreatePrefsDirectory(); return (siResult == 0); } //CreatePrefDirectory // --------------------------------------------------------------------------- // * IsOKToServiceQuery // --------------------------------------------------------------------------- bool CPlugInList::IsOKToServiceQuery( const char *inPluginName, const char *inNodeName, const char *inRecordTypeList, UInt32 inNumberRecordTypes ) { bool isOK = true; if (inRecordTypeList == NULL) //can't see this ever happening as we check before calling this routine return(isOK); fMutex.WaitLock(); if (fCFRecordTypeRestrictions != NULL && inPluginName != NULL) { CFStringRef cfPluginName = NULL; cfPluginName = CFStringCreateWithCString( kCFAllocatorDefault, inPluginName, kCFStringEncodingUTF8 ); if ( CFDictionaryContainsKey( fCFRecordTypeRestrictions, cfPluginName ) ) //plugin entry is in the dictionary { CFDictionaryRef cfPluginRestrictions = NULL; cfPluginRestrictions = (CFDictionaryRef)CFDictionaryGetValue( fCFRecordTypeRestrictions, cfPluginName ); if (inNodeName != NULL) //we have a node name that we can check for { CFStringRef cfNodeName = NULL; cfNodeName = CFStringCreateWithCString( kCFAllocatorDefault, inNodeName, kCFStringEncodingUTF8 ); bool useRestrictions = false; if ( CFDictionaryContainsKey( cfPluginRestrictions, cfNodeName ) ) //nodename entry is in the dictionary { useRestrictions = true; } else if ( CFDictionaryContainsKey( cfPluginRestrictions, CFSTR("General") ) ) //General entry is in the dictionary { DSCFRelease(cfNodeName); cfNodeName = CFStringCreateWithCString( kCFAllocatorDefault, "General", kCFStringEncodingUTF8 ); useRestrictions = true; } if (useRestrictions) { //the record type list requested CFStringRef cfRecordTypeList = NULL; cfRecordTypeList = CFStringCreateWithCString( kCFAllocatorDefault, inRecordTypeList, kCFStringEncodingUTF8 ); //get the restrictions dictionary CFDictionaryRef cfRestrictions = NULL; cfRestrictions = (CFDictionaryRef)CFDictionaryGetValue( cfPluginRestrictions, cfNodeName ); if ( CFDictionaryContainsKey( cfRestrictions, CFSTR(kRTRAllowKey) ) ) //Allow record type entry is in the dictionary { isOK = false; //init to false since we look over allowed record types //get the allow record type array CFArrayRef cfAllowRecordTypes = NULL; cfAllowRecordTypes = (CFArrayRef)CFDictionaryGetValue( cfRestrictions, CFSTR(kRTRAllowKey) ); CFIndex cfNumberRecordTypesInArray = CFArrayGetCount( cfAllowRecordTypes ); UInt32 countMatchesFound = 0; for( CFIndex i = 0; ifName = inEntry->fName; outEntry->fVersion = inEntry->fVersion; outEntry->fConfigAvail = inEntry->fConfigAvail; outEntry->fConfigFile = inEntry->fConfigFile; outEntry->fPluginPtr = inEntry->fPluginPtr; outEntry->fPluginRef = inEntry->fPluginRef; outEntry->fCFuuidFactory = inEntry->fCFuuidFactory; outEntry->fULVers = inEntry->fULVers; outEntry->fKey = inEntry->fKey; outEntry->fState = inEntry->fState; outEntry->fValidDataStamp = inEntry->fValidDataStamp; outEntry->pNext = nil; return(outEntry); }