/* * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * 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 CNSLPlugin */ #include "CNSLHeaders.h" #include // for dsTimeStamp #include "CNSLTimingUtils.h" #ifndef kServerRunLoop #define kServerRunLoop (eDSPluginCalls)(kHandleNetworkTransition + 1) #endif // since our plugins are now lazily loaded, we don't need to wait for NSL to activate us. #define DONT_WAIT_FOR_NSL_TO_ACTIVATE_US int LogHexDump(char *pktPtr, long pktLen); boolean_t NetworkChangeCallBack(SCDynamicStoreRef session, void *callback_argument); // These are the CFContainer callback protos void NSLReleaseNodeData( CFAllocatorRef allocator, const void* value ); CFStringRef NSLNodeValueCopyDesctriptionCallback ( const void *item ); Boolean NSLNodeValueEqualCallback ( const void *value1, const void *value2 ); void NSLNodeHandlerFunction(const void *inKey, const void *inValue, void *inContext); typedef struct AttrDataContext { sInt32 count; CDataBuff* attrDataBuf; Boolean attrOnly; } AttrDataContext; void AddToAttrData(const void *key, const void *value, void *context); sInt16 gOutstandingSearches = 0; pthread_mutex_t gOutstandingSearchesLock = PTHREAD_MUTEX_INITIALIZER; CNSLPlugin* CNSLPlugin::gsTheNSLPlugin = NULL; void SearchStarted( void ) { pthread_mutex_lock(&gOutstandingSearchesLock); gOutstandingSearches++; pthread_mutex_unlock(&gOutstandingSearchesLock); DBGLOG( "SearchStarted, gOutstandingSearches=%d\n", gOutstandingSearches ); } void SearchCompleted( void ) { Boolean okToStartNextSearch = false; pthread_mutex_lock(&gOutstandingSearchesLock); gOutstandingSearches--; DBGLOG( "SearchCompleted, gOutstandingSearches=%d\n", gOutstandingSearches ); pthread_mutex_unlock(&gOutstandingSearchesLock); if ( okToStartNextSearch ) { CNSLPlugin::TheNSLPlugin()->LockSearchQueue(); CNSLPlugin::TheNSLPlugin()->StartNextQueuedSearch(); // just give this a chance to fire one off CNSLPlugin::TheNSLPlugin()->UnlockSearchQueue(); } } #pragma mark - CFStringRef NSLQueuedSearchesCopyDesctriptionCallback ( const void *item ) { return kDSNSLQueuedSearchSAFE_CFSTR; } Boolean NSLQueuedSearchesEqualCallback ( const void *item1, const void *item2 ) { return item1 == item2; } NodeData * AllocateNodeData() { NodeData *newNode = (NodeData*)calloc( 1, sizeof(NodeData) ); return newNode; } void DeallocateNodeData( NodeData *nodeData ) { if ( nodeData != NULL ) { if ( nodeData->fNodeName ) ::CFRelease( nodeData->fNodeName ); nodeData->fNodeName = NULL; if ( nodeData->fServicesRefTable ) ::CFRelease( nodeData->fServicesRefTable ); nodeData->fServicesRefTable = NULL; if ( nodeData->fDSName ) { dsDataListDeallocatePriv( nodeData->fDSName ); free( nodeData->fDSName ); nodeData->fDSName = NULL; } free( nodeData ); } } /************************************************ * * Subclass needs to implement the following... * ************************************************ extern "C" { CFUUIDRef ModuleFactoryUUID = CFUUIDGetConstantUUIDWithBytes ( NULL, \ 0xD9, 0x70, 0xD5, 0x2E, 0xD5, 0x15, 0x11, 0xD3, \ 0x9F, 0xF9, 0x00, 0x05, 0x02, 0xC1, 0xC7, 0x36 ); } static CDSServerModule* _Creator ( void ) { return( new CNSLPlugin ); } CDSServerModule::tCreator CDSServerModule::sCreator = _Creator; */ CNSLPlugin::CNSLPlugin( void ) { DBGLOG( "CNSLPlugin::CNSLPlugin\n" ); mPublishedNodes = NULL; mLastNodeLookupStartTime = 0; mOpenRefTable = NULL; mActivatedByNSL = false; mDSLocalNodeLabel = NULL; mDSNetworkNodeLabel = NULL; mSearchQueue = NULL; mRunLoopRef = NULL; mTimerRef = NULL; mNodeLookupTimerRef = NULL; mSearchTicklerInstalled = false; mNodeLookupTimerInstalled = false; mState = kUnknownState; gsTheNSLPlugin = this; } // CNSLPlugin CNSLPlugin::~CNSLPlugin( void ) { DBGLOG( "CNSLPlugin::~CNSLPlugin\n" ); if ( mTimerRef ) { UnInstallSearchTickler(); } if ( mNodeLookupTimerRef ) { UnInstallNodeLookupTimer(); } if ( mPublishedNodes ) { ClearOutAllNodes(); ::CFDictionaryRemoveAllValues( mPublishedNodes ); ::CFRelease( mPublishedNodes ); mPublishedNodes = NULL; } if ( mOpenRefTable ) { ::CFDictionaryRemoveAllValues( mOpenRefTable ); ::CFRelease( mOpenRefTable ); mOpenRefTable = NULL; } if ( mSearchQueue ) ::CFRelease( mSearchQueue ); } // ~CNSLPlugin // -------------------------------------------------------------------------------- // * Validate () // -------------------------------------------------------------------------------- sInt32 CNSLPlugin::Validate ( const char *inVersionStr, const uInt32 inSignature ) { mSignature = inSignature; return( noErr ); } // Validate // -------------------------------------------------------------------------------- // * Initialize () // -------------------------------------------------------------------------------- sInt32 CNSLPlugin::Initialize( void ) { sInt32 siResult = eDSNoErr; DBGLOG( "CNSLPlugin::Initialize\n" ); // database initialization CFDictionaryKeyCallBacks keyCallBack; CFDictionaryValueCallBacks valueCallBack; valueCallBack.version = 0; valueCallBack.retain = NULL; valueCallBack.release = NSLReleaseNodeData; valueCallBack.copyDescription = NSLNodeValueCopyDesctriptionCallback; valueCallBack.equal = NSLNodeValueEqualCallback; if ( !mPublishedNodes ) mPublishedNodes = ::CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &valueCallBack); CFArrayCallBacks callBack; callBack.version = 0; callBack.retain = NULL; callBack.release = NULL; callBack.copyDescription = NSLQueuedSearchesCopyDesctriptionCallback; callBack.equal = NSLQueuedSearchesEqualCallback; if ( !mSearchQueue ) mSearchQueue= ::CFArrayCreateMutable( NULL, 0, &callBack ); pthread_mutex_init( &mQueueLock, NULL ); pthread_mutex_init( &mPluginLock, NULL ); pthread_mutex_init( &mOpenRefTableLock, NULL ); pthread_mutex_init( &mSearchQueueLock, NULL ); pthread_mutex_init( &mSearchLookupTimerLock, NULL ); pthread_mutex_init( &mNodeLookupTimerLock, NULL ); // use these for the reftable dictionary keyCallBack.version = 0; keyCallBack.retain = NULL; keyCallBack.release = NULL; keyCallBack.copyDescription = NULL; keyCallBack.equal = NULL; keyCallBack.hash = NULL; valueCallBack.release = NULL; valueCallBack.copyDescription = NULL; valueCallBack.equal = NULL; if ( !mOpenRefTable ) mOpenRefTable = ::CFDictionaryCreateMutable( NULL, 0, &keyCallBack, &valueCallBack ); if ( !mOpenRefTable ) DBGLOG("************* mOpenRefTable is NULL ***************\n"); if ( !siResult ) { // set the init flags mState = kInitialized | kInactive; // don't start a periodic task until we get activated } if ( !siResult ) siResult = InitPlugin(); // initialize our subclass return siResult; } // Initialize // --------------------------------------------------------------------------- // * WaitForInit // // --------------------------------------------------------------------------- void CNSLPlugin::WaitForInit( void ) { volatile uInt32 uiAttempts = 0; while ( !(mState & kInitialized) && !(mState & kFailedToInit) ) { // Try for 2 minutes before giving up if ( uiAttempts++ >= 240 ) { return; } // Now wait until we are told that there is work to do or // we wake up on our own and we will look for ourselves SmartSleep( (uInt32)(50000) ); } } // WaitForInit Boolean CNSLPlugin::IsActive( void ) { Boolean isActive = ((mState & kActive) || !(mState & kInactive)); return isActive; } #pragma mark - void PeriodicTimerCallback( CFRunLoopTimerRef timer, void *info ); void PeriodicTimerCallback( CFRunLoopTimerRef timer, void *info ) { CNSLPlugin* plugin = (CNSLPlugin*)info; plugin->NSLSearchTickler(); } void CNSLPlugin::InstallSearchTickler( void ) { if ( !mSearchTicklerInstalled && mRunLoopRef ) { DBGLOG( "CNSLPlugin::InstallSearchTickler (%s)\n", GetProtocolPrefixString() ); CFRunLoopTimerContext c = {0, this, NULL, NULL, NULL}; if ( !mTimerRef ) mTimerRef = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent()+kTaskInterval, 0, 0, 0, PeriodicTimerCallback, (CFRunLoopTimerContext*)&c); CFRunLoopAddTimer(mRunLoopRef, mTimerRef, kCFRunLoopDefaultMode); } mSearchTicklerInstalled = true; } void CNSLPlugin::UnInstallSearchTickler( void ) { if ( mSearchTicklerInstalled && mRunLoopRef ) { DBGLOG( "CNSLPlugin::UnInstallSearchTickler (%s)\n", GetProtocolPrefixString() ); if ( mTimerRef ) { CFRunLoopRemoveTimer( mRunLoopRef, mTimerRef, kCFRunLoopDefaultMode ); CFRelease( mTimerRef ); mTimerRef = NULL; } } mSearchTicklerInstalled = false; } sInt32 CNSLPlugin::NSLSearchTickler( void ) { sInt32 siResult = eDSNoErr; LockSearchQueue(); if ( IsActive() && mActivatedByNSL ) { DBGLOG( "CNSLPlugin::NSLSearchTickler, Outstanding Searches: %ld, queued Searches: %ld\n", NumOutstandingSearches(), (mSearchQueue)?CFArrayGetCount(mSearchQueue):-1 ); while ( NumOutstandingSearches() < kMaxNumOutstandingSearches && mSearchQueue && CFArrayGetCount(mSearchQueue) > 0 ) StartNextQueuedSearch(); // just give this a chance to fire one off if ( NumOutstandingSearches() == 0 ) UnInstallSearchTickler(); if ( mSearchTicklerInstalled ) CFRunLoopTimerSetNextFireDate( mTimerRef, CFAbsoluteTimeGetCurrent()+kTaskInterval ); } else if ( mSearchTicklerInstalled ) UnInstallSearchTickler(); UnlockSearchQueue(); return( siResult ); } // NSLTickler void CNSLPlugin::CancelCurrentlyQueuedSearches( void ) { LockSearchQueue(); while ( mSearchQueue && CFArrayGetCount(mSearchQueue) > 0 ) { CNSLServiceLookupThread* newLookup = (CNSLServiceLookupThread*)::CFArrayGetValueAtIndex( mSearchQueue, 0 ); ::CFArrayRemoveValueAtIndex( mSearchQueue, 0 ); delete( newLookup ); } UnlockSearchQueue(); } #pragma mark - void NodeLookupTimerCallback( CFRunLoopTimerRef timer, void *info ); void NodeLookupTimerCallback( CFRunLoopTimerRef timer, void *info ) { CNSLPlugin* plugin = (CNSLPlugin*)info; plugin->PeriodicNodeLookupTask(); } void CNSLPlugin::InstallNodeLookupTimer( void ) { LockNodeLookupTimer(); if ( !mNodeLookupTimerInstalled && mActivatedByNSL && mRunLoopRef ) { DBGLOG( "CNSLPlugin::InstallNodeLookupTimer (%s) called\n", GetProtocolPrefixString() ); CFRunLoopTimerContext c = {0, this, NULL, NULL, NULL}; if ( !mNodeLookupTimerRef ) mNodeLookupTimerRef = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent()+kNodeTimerIntervalImmediate, kKeepTimerAroundAfterFiring, 0, 0, NodeLookupTimerCallback, (CFRunLoopTimerContext*)&c); CFRunLoopAddTimer(mRunLoopRef, mNodeLookupTimerRef, kCFRunLoopDefaultMode); } mNodeLookupTimerInstalled = true; UnlockNodeLookupTimer(); } void CNSLPlugin::UnInstallNodeLookupTimer( void ) { LockNodeLookupTimer(); if ( mNodeLookupTimerInstalled && mRunLoopRef ) { DBGLOG( "CNSLPlugin::UnInstallNodeLookupTimer (%s)\n", GetProtocolPrefixString() ); if ( mNodeLookupTimerRef ) { CFRunLoopRemoveTimer( mRunLoopRef, mNodeLookupTimerRef, kCFRunLoopDefaultMode ); CFRelease( mNodeLookupTimerRef ); mNodeLookupTimerRef = NULL; } } mNodeLookupTimerInstalled = false; UnlockNodeLookupTimer(); } void CNSLPlugin::ResetNodeLookupTimer( UInt32 timeTillNewLookup ) { LockNodeLookupTimer(); if ( mNodeLookupTimerInstalled && mRunLoopRef ) { DBGLOG( "CNSLPlugin::ResetNodeLookupTimer (%s) setting timer to fire %d seconds from now\n", GetProtocolPrefixString(), timeTillNewLookup ); CFRunLoopTimerSetNextFireDate( mNodeLookupTimerRef, CFAbsoluteTimeGetCurrent() + timeTillNewLookup ); } UnlockNodeLookupTimer(); } void CNSLPlugin::PeriodicNodeLookupTask( void ) { if ( mActivatedByNSL && IsActive() ) { DBGLOG( "CNSLPlugin::PeriodicNodeLookupTask (%s), time to start new node lookup\n", GetProtocolPrefixString() ); StartNodeLookup(); } else { DBGLOG( "CNSLPlugin::PeriodicNodeLookupTask (%s), called but mActivatedByNSL== %d && IsActive() == %d!\n", GetProtocolPrefixString(), mActivatedByNSL, IsActive() ); } } // PeriodicNodeLookupTask #pragma mark - sInt32 CNSLPlugin::ProcessRequest ( void *inData ) { sInt32 siResult = 0; WaitForInit(); if ( inData == nil ) { DBGLOG( "CNSLPlugin::ProcessRequest, inData is NULL!\n" ); return( ePlugInDataError ); } if ( (mState & kFailedToInit) ) { DBGLOG( "CNSLPlugin::ProcessRequest, kFailedToInit!\n" ); return( ePlugInFailedToInitialize ); } if ( ((sHeader *)inData)->fType == kServerRunLoop ) { DBGLOG( "CNSLPlugin::ProcessRequest, received a RunLoopRef, 0x%x\n",((sHeader *)inData)->fContextData ); return SetServerIdleRunLoopRef( (CFRunLoopRef)(((sHeader *)inData)->fContextData) ); } else if ( !IsActive() ) { if ( ((sHeader *)inData)->fType == kOpenDirNode ) { // these are ok when we are inactive if this is the top level char *pathStr = nil; tDataListPtr pNodeList = nil; char *protocolStr = nil; pNodeList = ((sOpenDirNode *)inData)->fInDirNodeName; pathStr = dsGetPathFromListPriv( pNodeList, (char *)"/" ); protocolStr = pathStr + 1; if ( strstr( protocolStr, "NSLActivate" ) ) { // we have a directed open for our plugins, just set mActivated to true mActivatedByNSL = true; free( pathStr ); return( ePlugInNotActive ); } else if ( pathStr && GetProtocolPrefixString() && strcmp( protocolStr, GetProtocolPrefixString() ) == 0 ) { DBGLOG( "CNSLPlugin::ProcessRequest (kOpenDirNode), plugin not active, open on (%s) ok\n", protocolStr ); free( pathStr ); } else { DBGLOG( "CNSLPlugin::ProcessRequest (kOpenDirNode), plugin not active, returning ePlugInNotActive on open (%s)\n", protocolStr ); free( pathStr ); return( ePlugInNotActive ); } } else if ( ((sHeader *)inData)->fType == kCloseDirNode ) { DBGLOG( "CNSLPlugin::ProcessRequest (kCloseDirNode), plugin not active, returning noErr\n" ); } else if ( ((sHeader *)inData)->fType != kDoPlugInCustomCall ) { DBGLOG( "CNSLPlugin::ProcessRequest (%d), plugin not active!\n", ((sHeader *)inData)->fType ); return( ePlugInNotActive ); } } siResult = HandleRequest( inData ); return( siResult ); } // ProcessRequest #pragma mark - sInt32 CNSLPlugin::SetServerIdleRunLoopRef( CFRunLoopRef idleRunLoopRef ) { mRunLoopRef = idleRunLoopRef; return eDSNoErr; } // -------------------------------------------------------------------------------- // * SetPluginState () // -------------------------------------------------------------------------------- sInt32 CNSLPlugin::SetPluginState ( const uInt32 inState ) { // don't allow any changes other than active / in-active WaitForInit(); DBGLOG( "CNSLPlugin::SetPluginState(%s):", GetProtocolPrefixString() ); if ( (kActive & inState) && (mState & kInactive) ) // want to set to active only if currently inactive { DBGLOG( "kActive\n" ); if ( mState & kInactive ) mState -= kInactive; if ( !(mState & kActive) ) mState += kActive; #ifdef DONT_WAIT_FOR_NSL_TO_ACTIVATE_US mActivatedByNSL = true; // for now we are activating ourselves #endif ActivateSelf(); } if ( (kInactive & inState) && (mState & kActive) ) // want to set to inactive only if currently active { DBGLOG( "kInactive\n" ); if ( !(mState & kInactive) ) mState += kInactive; if ( mState & kActive ) mState -= kActive; DeActivateSelf(); } return( eDSNoErr ); } // SetPluginState void CNSLPlugin::ActivateSelf( void ) { if ( mActivatedByNSL ) { ZeroLastNodeLookupStartTime(); InstallNodeLookupTimer(); } } void CNSLPlugin::DeActivateSelf( void ) { // we need to deregister all our nodes UnInstallSearchTickler(); UnInstallNodeLookupTimer(); ClearOutAllNodes(); } #pragma mark - void CNSLPlugin::AddNode( CFStringRef nodeNameRef, Boolean isLocalNode ) { if ( !nodeNameRef || !IsActive() ) return; char nodeString[1024] = {0}; CFIndex bufferLen = sizeof(nodeString); if ( CFStringGetCString( nodeNameRef, nodeString, bufferLen, kCFStringEncodingUTF8 ) ) { AddNode( nodeString, isLocalNode ); } else fprintf(stderr, "CNSLPlugin::AddNode, CFStringGetCString failed!"); } void CNSLPlugin::AddNode( const char* nodeName, Boolean isLocalNode ) { if ( !nodeName || !IsActive() ) return; NodeData* node = NULL; CFStringRef nodeRef = CFStringCreateWithCString( NULL, nodeName, kCFStringEncodingUTF8 ); bool isADefaultNode = false; bool isADefaultOnlyNode = false; if ( nodeRef ) { DBGLOG( "CNSLPlugin::AddNode (%s) called with %s\n", GetProtocolPrefixString(), nodeName ); LockPublishedNodes(); if ( ::CFDictionaryContainsKey( mPublishedNodes, nodeRef ) ) node = (NodeData*)::CFDictionaryGetValue( mPublishedNodes, nodeRef ); if ( isLocalNode || IsLocalNode( nodeName ) ) isADefaultNode = true; if ( isADefaultNode ) isADefaultOnlyNode = IsADefaultOnlyNode( nodeName ); if ( node && isADefaultNode != node->fIsADefaultNode ) { // this node is being republished and has a different default state. We will deregister the old one and create a new one. //fprintf( stderr, "CNSLPlugin::AddNode calling DSUnregisterNode (%s) as isADefaultNode:(%d), node->fIsADefaultNode:(%d)\n", nodeName, isADefaultNode, node->fIsADefaultNode ); // DSUnregisterNode( mSignature, node->fDSName ); // now taken care of when the node is released ::CFDictionaryRemoveValue( mPublishedNodes, node->fNodeName ); // remove it from the dictionary // DeallocateNodeData( node ); node = NULL; } if ( node ) { node->fTimeStamp = GetCurrentTime(); // update this node } else { // we have a new node DBGLOG( "CNSLPlugin::AddNode(%s) Adding new %snode %s\n", GetProtocolPrefixString(), (isADefaultNode)?"local ":"", nodeName ); node = AllocateNodeData(); node->fNodeName = nodeRef; CFRetain( node->fNodeName ); node->fDSName = dsBuildListFromStringsPriv(GetProtocolPrefixString(), nodeName, nil); node->fTimeStamp = GetCurrentTime(); node->fServicesRefTable = ::CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); node->fIsADefaultNode = isADefaultNode; node->fSignature = mSignature; if ( node->fDSName ) { eDirNodeType nodeTypeToRegister = kUnknownNodeType; if ( isADefaultNode ) nodeTypeToRegister = kDefaultNetworkNodeType; if ( !isADefaultOnlyNode ) nodeTypeToRegister = (eDirNodeType)( nodeTypeToRegister | kDirNodeType ); if ( nodeTypeToRegister != kUnknownNodeType ) DSRegisterNode( mSignature, node->fDSName, nodeTypeToRegister ); } ::CFDictionaryAddValue( mPublishedNodes, nodeRef, node ); } CFRelease( nodeRef ); UnlockPublishedNodes(); } } void CNSLPlugin::RemoveNode( CFStringRef nodeNameRef ) { NodeData* node = NULL; DBGLOG( "CNSLPlugin::RemoveNode called with" ); if ( getenv("NSLDEBUG") ) CFShow(nodeNameRef); LockPublishedNodes(); Boolean containsNode = ::CFDictionaryContainsKey( mPublishedNodes, nodeNameRef ); if ( containsNode ) { node = (NodeData*)::CFDictionaryGetValue( mPublishedNodes, nodeNameRef ); // now taken care of in the release // DSUnregisterNode( mSignature, node->fDSName ); ::CFDictionaryRemoveValue( mPublishedNodes, node->fNodeName ); // remove it from the dictionary // now taken care of in the release // DeallocateNodeData( node ); node = NULL; } UnlockPublishedNodes(); } void CNSLPlugin::NodeLookupComplete( void ) { DBGLOG( "CNSLPlugin::NodeLookupComplete\n" ); ClearOutStaleNodes(); // get rid of any that are old } // NodeLookupComplete const char* CNSLPlugin::GetLocalNodeString( void ) { return NULL; // plugin should override } Boolean CNSLPlugin::IsLocalNode( const char *inNode ) { #pragma unused (inNode) return false; // plugin should override } Boolean CNSLPlugin::IsADefaultOnlyNode( const char *inNode ) { #pragma unused (inNode) return false; // plugin should override } #pragma mark - CFStringRef NetworkChangeNSLCopyStringCallback( const void *item ); CFStringRef NetworkChangeNSLCopyStringCallback( const void *item ) { return kNetworkChangeNSLCopyStringCallbackSAFE_CFSTR; } static void DoNetworkTransition(CFRunLoopTimerRef timer, void *info); void DoNetworkTransition(CFRunLoopTimerRef timer, void *info) { if ( info != nil ) { //do something if the wait period has passed ((CNSLPlugin *)info)->HandleNetworkTransitionIfTime(); } }// DoNIPINetworkChange #pragma mark - // --------------------------------------------------------------------------- // * HandleRequest // // --------------------------------------------------------------------------- sInt32 CNSLPlugin::HandleRequest( void *inData ) { sInt32 siResult = 0; sHeader *pMsgHdr = nil; if ( inData == nil ) { return( -8088 ); } pMsgHdr = (sHeader *)inData; switch ( pMsgHdr->fType ) { case kOpenDirNode: siResult = OpenDirNode( (sOpenDirNode *)inData ); break; case kCloseDirNode: siResult = CloseDirNode( (sCloseDirNode *)inData ); break; case kGetDirNodeInfo: siResult = GetDirNodeInfo( (sGetDirNodeInfo *)inData ); break; case kGetRecordList: siResult = GetRecordList( (sGetRecordList *)inData ); break; case kGetRecordEntry: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kGetRecordEntry yet\n" ); siResult = eNotHandledByThisNode; break; case kGetAttributeEntry: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kGetAttributeEntry yet\n" ); siResult = eNotHandledByThisNode; break; case kGetAttributeValue: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kGetAttributeValue yet\n" ); siResult = eNotHandledByThisNode; break; case kOpenRecord: siResult = OpenRecord( (sOpenRecord *)inData ); break; case kGetRecordReferenceInfo: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kGetRecordReferenceInfo yet\n" ); siResult = eNotHandledByThisNode; break; case kGetRecordAttributeInfo: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kGetRecordAttributeInfo yet\n" ); siResult = eNotHandledByThisNode; break; case kGetRecordAttributeValueByID: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kGetRecordAttributeValueByID yet\n" ); siResult = eNotHandledByThisNode; break; case kGetRecordAttributeValueByIndex: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kGetRecordAttributeValueByIndex yet\n" ); siResult = eNotHandledByThisNode; break; case kFlushRecord: siResult = FlushRecord( (sFlushRecord *)inData ); break; case kCloseAttributeList: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kCloseAttributeList yet\n" ); siResult = eNotHandledByThisNode; break; case kCloseAttributeValueList: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kCloseAttributeValueList yet\n" ); siResult = eNotHandledByThisNode; break; case kCloseRecord: siResult = CloseRecord( (sCloseRecord*)inData ); break; case kSetRecordName: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kSetRecordName yet\n" ); siResult = eNotHandledByThisNode; break; case kSetRecordType: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kSetRecordType yet\n" ); siResult = eNotHandledByThisNode; break; case kDeleteRecord: siResult = DeleteRecord( (sDeleteRecord*)inData ); break; case kCreateRecord: case kCreateRecordAndOpen: siResult = CreateRecord( (sCreateRecord *)inData ); break; case kAddAttribute: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kAddAttribute yet\n" ); siResult = eNotHandledByThisNode; break; case kRemoveAttribute: siResult = RemoveAttribute( (sRemoveAttribute*)inData ); break; case kAddAttributeValue: DBGLOG( "CNSLPlugin::HandleRequest, calling AddAttributeValue\n" ); siResult = AddAttributeValue( (sAddAttributeValue *)inData ); break; case kRemoveAttributeValue: siResult = RemoveAttributeValue( (sRemoveAttributeValue*)inData ); break; case kSetAttributeValue: siResult = SetAttributeValue( (sSetAttributeValue*)inData ); break; case kDoDirNodeAuth: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kDoDirNodeAuth yet\n" ); siResult = eNotHandledByThisNode; break; case kDoAttributeValueSearch: case kDoAttributeValueSearchWithData: DBGLOG( "CNSLPlugin::HandleRequest, we don't handle kDoAttributeValueSearch or kDoAttributeValueSearchWithData yet\n" ); siResult = eNotHandledByThisNode; break; case kDoPlugInCustomCall: siResult = DoPlugInCustomCall( (sDoPlugInCustomCall *)inData ); break; case kHandleNetworkTransition: DBGLOG( "CNSLPlugin::HandleRequest, we have some sort of network transition\n" ); //let us be smart about doing the recheck //we don't want to re-init multiple times during this wait period //however we do go ahead and fire off timers each time //each call in here we update the delay time by 5 seconds mTransitionCheckTime = time(nil) + 5; if (mRunLoopRef != nil) { CFRunLoopTimerContext c = {0, (void*)this, NULL, NULL, NetworkChangeNSLCopyStringCallback}; CFRunLoopTimerRef timer = CFRunLoopTimerCreate( NULL, CFAbsoluteTimeGetCurrent() + 5, 0, 0, 0, DoNetworkTransition, (CFRunLoopTimerContext*)&c); CFRunLoopAddTimer(mRunLoopRef, timer, kCFRunLoopDefaultMode); if (timer) CFRelease(timer); } // siResult = HandleNetworkTransition( (sHeader*)inData ); break; default: siResult = eNotHandledByThisNode; break; } pMsgHdr->fResult = siResult; if ( siResult ) DBGLOG( "CNSLPlugin::HandleRequest returning %ld on a request of type %ld\n", siResult, pMsgHdr->fType ); return( siResult ); } // HandleRequest sInt32 CNSLPlugin::DoPlugInCustomCall ( sDoPlugInCustomCall *inData ) { sInt32 siResult = eNotHandledByThisNode; // plugins can override return siResult; } sInt32 CNSLPlugin::GetDirNodeInfo( sGetDirNodeInfo *inData ) { sInt32 siResult = eNotHandledByThisNode; // plugins can override DBGLOG( "CNSLPlugin::GetDirNodeInfo not handled by this plugin\n" ); return siResult; } sInt32 CNSLPlugin::OpenDirNode ( sOpenDirNode *inData ) { sInt32 siResult = eDSNoErr; char *nodeName = nil; char *pathStr = nil; char *protocolStr = nil; tDataListPtr pNodeList = nil; const char* subStr = nil; Boolean weHandleThisNode = false; DBGLOG( "CNSLPlugin::OpenDirNode %lx\n", inData->fOutNodeRef ); if ( inData != nil ) { try { pNodeList = inData->fInDirNodeName; pathStr = dsGetPathFromListPriv( pNodeList, (char *)"/" ); protocolStr = pathStr + 1; // advance past the '/' DBGLOG( "CNSLPlugin::OpenDirNode, ProtocolPrefixString is %s, pathStr is %s\n", GetProtocolPrefixString(), pathStr ); if ( strstr( pathStr, "NSLActivate" ) ) { // we have a directed open for our plugins, fire off our node searches if needed if ( !mActivatedByNSL ) { mActivatedByNSL = true; if ( IsActive() ) // only start lookups if we are currently active { InstallNodeLookupTimer(); } } free( pathStr ); return eDSNoErr; } else if ( strcmp( protocolStr, GetProtocolPrefixString() ) == 0 ) { // they are opening this plugin at the top level so they'll probably be wanting to configure it nodeName = new char[1+strlen(protocolStr)]; if ( !nodeName ) throw ( eDSNullNodeName ); ::strcpy(nodeName,protocolStr); if ( nodeName ) { DBGLOG( "CNSLPlugin::OpenDirNode on %s\n", nodeName ); CNSLDirNodeRep* newNodeRep = new CNSLDirNodeRep( this, (const void*)inData->fOutNodeRef ); if (!newNodeRep) throw ( eDSNullNodeName ); newNodeRep->Initialize( nodeName, inData->fInUID, true ); newNodeRep->Retain(); // add the item to the reference table LockOpenRefTable(); ::CFDictionaryAddValue( mOpenRefTable, (const void*)inData->fOutNodeRef, (const void*)newNodeRep ); UnlockOpenRefTable(); delete( nodeName ); nodeName = nil; } free( pathStr ); return eDSNoErr; } else if ( IsActive() && (strlen(pathStr) > strlen(GetProtocolPrefixString()+1)) && (::strncmp(protocolStr, GetProtocolPrefixString(), strlen(GetProtocolPrefixString())) == 0) && protocolStr[strlen(GetProtocolPrefixString())] == '/' ) { // only work on nodes with our prefix subStr = pathStr + strlen(GetProtocolPrefixString()) + 2; CFStringRef nodeAsRef = ::CFStringCreateWithCString( NULL, subStr, kCFStringEncodingUTF8 ); if ( nodeAsRef ) { LockPublishedNodes(); weHandleThisNode = ::CFDictionaryContainsKey( mPublishedNodes, nodeAsRef ); UnlockPublishedNodes(); if ( weHandleThisNode ) DBGLOG( "CNSLPlugin::OpenDirNode, we will handle the request since we have this node (%s) already published\n", subStr ); else { if ( getenv("NSLDEBUG") ) { DBGLOG( "CNSLPlugin::OpenDirNode, we don't handle this node, ref/publishedNodes\n" ); if ( nodeAsRef ) CFShow(nodeAsRef); } } ::CFRelease( nodeAsRef ); if ( !weHandleThisNode ) { weHandleThisNode = OKToOpenUnPublishedNode( subStr ); // allow plugin option of searching unpublished nodes if ( weHandleThisNode ) { subStr = pathStr + strlen(GetProtocolPrefixString()) + 2; // prefix plus pre and post '/'s DBGLOG( "CNSLPlugin::OpenDirNode Adding new node %s\n", subStr ); CFStringRef nodeRef = CFStringCreateWithCString( NULL, subStr, kCFStringEncodingUTF8 ); if ( nodeRef ) { NodeData* node = AllocateNodeData(); node->fNodeName = nodeRef; CFRetain( node->fNodeName ); node->fDSName = dsBuildListFromStringsPriv(GetProtocolPrefixString(), subStr, nil); node->fTimeStamp = GetCurrentTime(); node->fServicesRefTable = ::CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); node->fIsADefaultNode = IsLocalNode( subStr ); node->fSignature = mSignature; if ( node->fDSName ) { eDirNodeType nodeTypeToRegister = kUnknownNodeType; if ( IsLocalNode( subStr ) ) nodeTypeToRegister = kDefaultNetworkNodeType; nodeTypeToRegister = (eDirNodeType)( nodeTypeToRegister | kDirNodeType ); if ( nodeTypeToRegister != kUnknownNodeType ) DSRegisterNode( mSignature, node->fDSName, nodeTypeToRegister ); } ::CFDictionaryAddValue( mPublishedNodes, nodeRef, node ); ::CFRelease( nodeRef ); } } } if ( !weHandleThisNode ) DBGLOG( "CNSLPlugin::OpenDirNode, we won't handle the request on this node (%s)\n", pathStr ); } } if ( weHandleThisNode ) { if ( !subStr ) subStr = pathStr + strlen(GetProtocolPrefixString()) + 2; // prefix plus pre and post '/'s if ( subStr ) { DBGLOG( "CNSLPlugin::OpenDirNode subStr is %s\n", subStr ); nodeName = new char[1+strlen(subStr)]; if( !nodeName ) throw ( eDSNullNodeName ); ::strcpy(nodeName,subStr); } if ( nodeName ) { DBGLOG( "CNSLPlugin::OpenDirNode on %s\n", nodeName ); CNSLDirNodeRep* newNodeRep = new CNSLDirNodeRep( this, (const void*)inData->fOutNodeRef ); if( !newNodeRep ) throw (eMemoryAllocError); newNodeRep->Initialize( nodeName, inData->fInUID, false ); newNodeRep->Retain(); // add the item to the reference table LockOpenRefTable(); ::CFDictionaryAddValue( mOpenRefTable, (const void*)inData->fOutNodeRef, (const void*)newNodeRep ); UnlockOpenRefTable(); delete( nodeName ); nodeName = nil; } else { inData->fOutNodeRef = 0; DBGLOG( "CNSLPlugin::OpenDirNode nodeName is NULL!\n" ); } } else { siResult = eNotHandledByThisNode; DBGLOG( "CNSLPlugin::OpenDirNode skipping cuz path is %s\n", pathStr ); } } catch( int err ) { siResult = err; DBGLOG( "CNSLPlugin::CloseDirNode, Caught error:%li\n", siResult ); } } else DBGLOG( "CNSLPlugin::OpenDirNode inData is NULL!\n" ); if (pathStr != NULL) { free(pathStr); pathStr = NULL; } if ( nodeName ) { delete( nodeName ); nodeName = nil; } return siResult; } sInt32 CNSLPlugin::CloseDirNode ( sCloseDirNode *inData ) { sInt32 siResult = eDSNoErr; DBGLOG( "CNSLPlugin::CloseDirNode %lx\n", inData->fInNodeRef ); try { if ( inData != nil && inData->fInNodeRef && mOpenRefTable ) { CNSLDirNodeRep* nodeRep = NULL; LockOpenRefTable(); nodeRep = (CNSLDirNodeRep*)::CFDictionaryGetValue( mOpenRefTable, (const void*)inData->fInNodeRef ); DBGLOG( "CNSLPlugin::CloseDirNode, CFDictionaryGetValue returned nodeRep: 0x%x\n", (void*)nodeRep ); if ( nodeRep ) { ::CFDictionaryRemoveValue( mOpenRefTable, (void*)inData->fInNodeRef ); DBGLOG( "CNSLPlugin::CloseDirNode, delete nodeRep: 0x%x\n", (void*)nodeRep ); nodeRep->Release(); } else { DBGLOG( "CNSLPlugin::CloseDirNode, nodeRef not found in our list\n" ); } UnlockOpenRefTable(); } } catch( int err ) { siResult = err; DBGLOG( "CNSLPlugin::CloseDirNode, Caught error:%li\n", siResult ); } return siResult; } sInt32 CNSLPlugin::GetRecordList ( sGetRecordList *inData ) { sInt32 siResult = eDSNoErr; char *pRecType = nil; char *pNSLRecType = nil; CAttributeList *cpRecNameList = nil; CAttributeList *cpRecTypeList = nil; CAttributeList *cpAttrTypeList = nil; CBuff *outBuff = nil; uInt32 countDownRecTypes = 0; const void* dictionaryResult = NULL; CNSLDirNodeRep* nodeDirRep = NULL; unsigned long incomingRecEntryCount = inData->fOutRecEntryCount; LockOpenRefTable(); DBGLOG( "CNSLPlugin::GetRecordList called\n" ); dictionaryResult = ::CFDictionaryGetValue( mOpenRefTable, (const void*)inData->fInNodeRef ); nodeDirRep = (CNSLDirNodeRep*)dictionaryResult; if( !nodeDirRep ) { DBGLOG( "CNSLPlugin::GetRecordList called but we couldn't find the nodeDirRep!\n" ); UnlockOpenRefTable(); return eDSInvalidNodeRef; } if ( nodeDirRep->IsTopLevelNode() ) { DBGLOG( "CNSLPlugin::GetRecordList called on a top level node, return no results\n" ); inData->fIOContinueData = NULL; inData->fOutRecEntryCount = 0; // no more entries to parse UnlockOpenRefTable(); return eDSNoErr; } nodeDirRep->Retain(); UnlockOpenRefTable(); inData->fOutRecEntryCount = 0; // make sure to reset this! try { if ( nodeDirRep && !nodeDirRep->IsLookupStarted() ) { DBGLOG( "CNSLPlugin::GetRecordList called, lookup hasn't been started yet.\n" ); // ok, we need to initialize this nodeDirRep and start the searches... // Verify all the parameters if( !inData ) throw( eMemoryError ); if( !inData->fInDataBuff) throw( eDSEmptyBuffer ); if( (inData->fInDataBuff->fBufferSize == 0) ) throw( eDSEmptyBuffer ); if( !inData->fInRecNameList ) throw( eDSEmptyRecordNameList ); if( !inData->fInRecTypeList ) throw( eDSEmptyRecordTypeList ); if( !inData->fInAttribTypeList ) throw( eDSEmptyAttributeTypeList ); //check if the client has requested a limit on the number of records to return //we only do this the first call into this context for pContext if (incomingRecEntryCount >= 0) { nodeDirRep->LimitRecSearch( incomingRecEntryCount ); } // Node context data // Get the record type list cpRecTypeList = new CAttributeList( inData->fInRecTypeList ); if( !cpRecTypeList ) throw( eDSEmptyRecordTypeList ); //save the number of rec types here to use in separating the buffer data countDownRecTypes = cpRecTypeList->GetCount(); if( (countDownRecTypes == 0) ) throw( eDSEmptyRecordTypeList ); sInt32 error = eDSNoErr; nodeDirRep->LookupHasStarted(); // performance hack... Boolean alreadyLookedupSMBType = false; for ( uInt16 i=1; i<=countDownRecTypes; i++ ) { if ( (error = cpRecTypeList->GetAttribute( i, &pRecType )) == eDSNoErr ) { DBGLOG( "CNSLPlugin::GetRecordList, GetAttribute returned pRecType:%s\n", pRecType ); if ( pRecType ) { if ( strcmp( pRecType, kDSStdRecordTypeSMBServer ) == 0 ) { if ( alreadyLookedupSMBType ) { free( pNSLRecType ); continue; // already looked up one of these } alreadyLookedupSMBType = true; } pNSLRecType = CreateNSLTypeFromRecType( pRecType ); if ( pNSLRecType ) { DBGLOG( "CNSLPlugin::GetRecordList, CreateNSLTypeFromRecType returned pNSLRecType:%s\n", pNSLRecType ); StartServicesLookup( pNSLRecType, nodeDirRep ); free( pNSLRecType ); inData->fIOContinueData = (void*)inData->fInNodeRef; // just to show we have continuing data } } } else { DBGLOG( "CNSLPlugin::GetRecordList, GetAttribute returned error:%li\n", error ); } } } else if ( nodeDirRep && nodeDirRep->HaveResults() ) { // cool, we have data waiting for us... // we already started a search, check for results. siResult = RetrieveResults( inData, nodeDirRep ); inData->fIOContinueData = (void*)inData->fInNodeRef; // just to show we have continuing data DBGLOG( "***Sending Back Results, fBufferSize = %ld, fBufferLength = %ld, inData->fOutRecEntryCount = %d\n", inData->fInDataBuff->fBufferSize, inData->fInDataBuff->fBufferLength, inData->fOutRecEntryCount ); } else if ( nodeDirRep && nodeDirRep->LookupComplete() ) { DBGLOG( "CNSLPlugin::GetRecordList called and nodeDirRep->LookupComplete so setting inData->fIOContinueData to NULL\n" ); inData->fIOContinueData = NULL; nodeDirRep->ResetLookupHasStarted(); // so users can start another search } else SmartSleep( 100000 ); // sleep for a 10th of a second so the client can't spinlock } // try catch ( int err ) { siResult = err; DBGLOG( "CNSLPlugin::GetRecordList, Caught error:%li\n", siResult ); } LockOpenRefTable(); if ( nodeDirRep ) nodeDirRep->Release(); UnlockOpenRefTable(); if ( cpRecNameList != nil ) { delete( cpRecNameList ); cpRecNameList = nil; } if ( cpRecTypeList != nil ) { delete( cpRecTypeList ); cpRecTypeList = nil; } if ( cpAttrTypeList != nil ) { delete( cpAttrTypeList ); cpAttrTypeList = nil; } if ( outBuff != nil ) { delete( outBuff ); outBuff = nil; } return( siResult ); } // GetRecordList sInt32 CNSLPlugin::OpenRecord ( sOpenRecord *inData ) { sInt32 siResult = eDSRecordNotFound; // by default we don't Open records const void* dictionaryResult = NULL; DBGLOG( "CNSLPlugin::OpenRecord called on refNum:0x%x\n", inData->fOutRecRef ); LockOpenRefTable(); dictionaryResult = ::CFDictionaryGetValue( mOpenRefTable, (const void*)inData->fInNodeRef ); if ( !dictionaryResult ) { UnlockOpenRefTable(); DBGLOG( "CNSLPlugin::OpenRecord called but we couldn't find the nodeDirRep!\n" ); return eDSInvalidNodeRef; } ((CNSLDirNodeRep*)dictionaryResult)->Retain(); UnlockOpenRefTable(); CFStringRef nodeNameRef = ((CNSLDirNodeRep*)dictionaryResult)->GetNodeName(); char* pNSLRecType = CreateNSLTypeFromRecType( (char*)inData->fInRecType->fBufferData ); if ( pNSLRecType ) { CFMutableStringRef serviceKeyRef = ::CFStringCreateMutable( NULL, 0 ); // we'll use this as a key CFStringRef recordTypeRef = ::CFStringCreateWithCString( kCFAllocatorDefault, pNSLRecType, kCFStringEncodingUTF8 ); CFStringRef recordNameRef = ::CFStringCreateWithCString( kCFAllocatorDefault, (char*)inData->fInRecName->fBufferData, kCFStringEncodingUTF8 ); if ( serviceKeyRef && recordTypeRef && recordNameRef ) { ::CFStringAppend( serviceKeyRef, recordNameRef ); ::CFStringAppend( serviceKeyRef, recordTypeRef ); } if ( recordNameRef ) CFRelease( recordNameRef ); if ( recordTypeRef ) CFRelease( recordTypeRef ); free( pNSLRecType ); LockPublishedNodes(); NodeData* node = (NodeData*)::CFDictionaryGetValue( mPublishedNodes, nodeNameRef ); if ( node ) { if ( getenv("NSLDEBUG") ) { DBGLOG( "CNSLPlugin::OpenRecord, found the node in question\n" ); CFShow( nodeNameRef ); DBGLOG( "CNSLPlugin::OpenRecord, looking up record\n" ); CFShow( serviceKeyRef ); } CFDictionaryRef recordRef = (CFDictionaryRef)::CFDictionaryGetValue( node->fServicesRefTable, serviceKeyRef ); if ( recordRef ) CFRetain( recordRef ); // hold on to this while we manipulate it and release at the end. UnlockPublishedNodes(); if ( recordRef && CFGetTypeID(recordRef) == CFDictionaryGetTypeID() ) { CFStringRef recordName = (CFStringRef)::CFDictionaryGetValue( recordRef, kDSNAttrRecordNameSAFE_CFSTR ); CFStringRef nodeName = (CFStringRef)::CFDictionaryGetValue( recordRef, kDS1AttrLocationSAFE_CFSTR ); if ( !recordName && !nodeName ) { if ( getenv("NSLDEBUG") ) { DBGLOG( "CNSLPlugin::OpenRecord, the node->fServicesRefTable doesn't have both recordName and nodeName keys!\n" ); if (recordName) CFShow( recordName ); else DBGLOG( "CNSLPlugin::OpenRecord, recordName is null\n" ); if (nodeName) CFShow( nodeName ); else DBGLOG( "CNSLPlugin::OpenRecord, nodeName is null\n" ); } } else if ( CFGetTypeID(recordName) == CFStringGetTypeID() && CFGetTypeID(nodeName) == CFStringGetTypeID() ) { CFRetain( recordRef ); // manually add retain when putting it into mOpenRefTable. // found it. LockOpenRefTable(); ::CFDictionaryAddValue( mOpenRefTable, (void*)inData->fOutRecRef, (void*)recordRef ); UnlockOpenRefTable(); siResult = eDSNoErr; if ( getenv("NSLDEBUG") ) { DBGLOG( "CNSLPlugin::OpenRecord, found the record in question (ref:0x%x)\n", inData->fOutRecRef ); } } else DBGLOG( "CNSLPlugin::OpenRecord, we just grabbed a name or node name that wasn't a CFString!\n" ); } else if ( recordRef ) DBGLOG( "CNSLPlugin::OpenRecord, we just grabbed something out of our node->fServicesRefTable that wasn't a CFDictionary!\n" ); else DBGLOG( "CNSLPlugin::OpenRecord, couldn't find previously created record\n" ); if ( recordRef ) CFRelease( recordRef ); // ok, we're done with this now. } else { UnlockPublishedNodes(); // do we want to allow two openings of a record? Or is this not possible as we should be getting a unique // ref assigned to this open?... DBGLOG( "CNSLPlugin::OpenRecord this record already opened...\n" ); siResult = eDSNoErr; } ::CFRelease( serviceKeyRef ); } ((CNSLDirNodeRep*)dictionaryResult)->Release(); return( siResult ); } // OpenRecord sInt32 CNSLPlugin::CloseRecord ( sCloseRecord *inData ) { sInt32 siResult = eDSNoErr; // by default we don't Close records DBGLOG( "CNSLPlugin::CloseRecord called on ref:%ld\n", inData->fInRecRef ); LockOpenRefTable(); CFDictionaryRef recordRef = (CFDictionaryRef)::CFDictionaryGetValue( mOpenRefTable, (const void*)inData->fInRecRef ); if ( recordRef ) { ::CFDictionaryRemoveValue( mOpenRefTable, (const void*)inData->fInRecRef ); CFRelease( recordRef ); } else { DBGLOG( "CNSLPlugin::CloseRecord called but the record wasn't found!\n" ); siResult = eDSRecordNotFound; } UnlockOpenRefTable(); return( siResult ); } // CloseRecord sInt32 CNSLPlugin::CreateRecord ( sCreateRecord *inData ) { sInt32 siResult = eDSNoErr; // by default we don't create records const void* dictionaryResult = NULL; CNSLDirNodeRep* nodeDirRep = NULL; tDataNodePtr pRecName = NULL; tDataNodePtr pRecType = NULL; char *pNSLRecType = NULL; CFMutableStringRef serviceKeyRef = NULL; CFMutableDictionaryRef newService = NULL; DBGLOG( "CNSLPlugin::CreateRecord called\n" ); DBGLOG( "CNSLPlugin::CreateRecord, fOutRecRef is 0x%x\n", inData->fOutRecRef ); DBGLOG( "CNSLPlugin::CreateRecord, fInOpen is %d\n", inData->fInOpen ); if ( ReadOnlyPlugin() ) { // ignore if this plugin doesn't support creation anyway return eDSReadOnly; } if ( !IsClientAuthorizedToCreateRecords( inData ) ) { return eDSPermissionError; } try { LockOpenRefTable(); dictionaryResult = ::CFDictionaryGetValue( mOpenRefTable, (const void*)inData->fInNodeRef ); if ( !dictionaryResult ) { UnlockOpenRefTable(); DBGLOG( "CNSLPlugin::CreateRecord called but we couldn't find the nodeDirRep!\n" ); return eDSInvalidNodeRef; } nodeDirRep = (CNSLDirNodeRep*)dictionaryResult; nodeDirRep->Retain(); UnlockOpenRefTable(); serviceKeyRef = ::CFStringCreateMutable( NULL, 0 ); // we'll use this as a key newService = ::CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); pRecType = inData->fInRecType; if( !pRecType ) throw( eDSNullRecType ); pRecName = inData->fInRecName; if( !pRecName ) throw( eDSNullRecName ); pNSLRecType = CreateNSLTypeFromRecType( (char*)pRecType->fBufferData ); if( !pNSLRecType ) throw( eDSInvalidRecordType ); if ( pNSLRecType ) { if ( getenv("NSLDEBUG") ) { DBGLOG( "CNSLPlugin::CreateRecord, CreateNSLTypeFromRecType returned pNSLRecType:%s\n", pNSLRecType ); DBGLOG( "dictionary contents before:\n"); CFShow( newService ); } CFStringRef keyRef, valueRef; // add node name keyRef = ::CFStringCreateWithCString( kCFAllocatorDefault, kDS1AttrLocation, kCFStringEncodingUTF8 ); valueRef = nodeDirRep->GetNodeName(); if ( !CFDictionaryContainsKey( newService, keyRef ) ) ::CFDictionaryAddValue( newService, keyRef, valueRef ); ::CFRelease( keyRef ); // add record name keyRef = ::CFStringCreateWithCString( kCFAllocatorDefault, kDSNAttrRecordName, kCFStringEncodingUTF8 ); valueRef = ::CFStringCreateWithCString( kCFAllocatorDefault, (char*)pRecName->fBufferData, kCFStringEncodingUTF8 ); if ( !CFDictionaryContainsKey( newService, keyRef ) ) ::CFDictionaryAddValue( newService, keyRef, valueRef ); ::CFStringAppend( serviceKeyRef, valueRef ); // key is made up of name and type ::CFRelease( keyRef ); ::CFRelease( valueRef ); // add record type keyRef = ::CFStringCreateWithCString( kCFAllocatorDefault, kDS1AttrServiceType, kCFStringEncodingUTF8 ); valueRef = ::CFStringCreateWithCString( kCFAllocatorDefault, pNSLRecType, kCFStringEncodingUTF8 ); ::CFDictionarySetValue( newService, keyRef, valueRef ); // set this regardless whether it is already there ::CFStringAppend( serviceKeyRef, valueRef ); // key is made up of name and type ::CFRelease( keyRef ); ::CFRelease( valueRef ); if ( getenv("NSLDEBUG") ) { DBGLOG( "dictionary contents after:\n"); CFShow( newService ); DBGLOG( "CNSLPlugin::CreateRecord, finished intial creation of opened service dictionary\n" ); if ( getenv( "NSLDEBUG" ) ) ::CFShow( newService ); } free( pNSLRecType ); // now we need to add this to our published node's dictionary of services LockPublishedNodes(); if ( ::CFDictionaryContainsKey( mPublishedNodes, nodeDirRep->GetNodeName() ) ) { NodeData* node = (NodeData*)::CFDictionaryGetValue( mPublishedNodes, nodeDirRep->GetNodeName() ); if ( node ) { ::CFDictionarySetValue( node->fServicesRefTable, serviceKeyRef, newService ); if ( getenv( "NSLDEBUG" ) ) { DBGLOG( "CNSLPlugin::CreateRecord, adding key to node\n" ); CFShow( serviceKeyRef ); } } else DBGLOG( "CNSLPlugin::CreateRecord, couldn't find node in our published nodes!\n" ); } UnlockPublishedNodes(); } } catch ( int err ) { siResult = err; } LockOpenRefTable(); nodeDirRep->Release(); UnlockOpenRefTable(); if ( serviceKeyRef ) CFRelease( serviceKeyRef ); if ( newService ) CFRelease( newService ); DBGLOG( "CNSLPlugin::CreateRecord, fOutRecRef is 0x%x\n", inData->fOutRecRef ); if ( !siResult && inData->fInOpen ) { sOpenRecord inCopyData = { inData->fType, inData->fResult, inData->fInNodeRef, inData->fInRecType, inData->fInRecName, inData->fOutRecRef }; siResult = OpenRecord( &inCopyData ); DBGLOG( "CNSLPlugin::CreateRecord, fOutRecRef after OpenRecord is 0x%x\n", inData->fOutRecRef ); } else DBGLOG( "CNSLPlugin::CreateRecord, siResult:%ld, inData->fInOpen=%d, fOutRecRef is 0x%x\n", siResult, inData->fInOpen, inData->fOutRecRef ); if ( kCreateRecordAndOpen == inData->fType ) DBGLOG( "CNSLPlugin::CreateRecord, this was supposed to be a kCreateRecordAndOpen\n"); return( siResult ); } // CreateRecord sInt32 CNSLPlugin::DeleteRecord ( sDeleteRecord *inData ) { sInt32 siResult = eDSNoErr; CFMutableDictionaryRef serviceToDeregister = NULL; DBGLOG( "CNSLPlugin::DeleteRecord called on refNum:%ld\n", inData->fInRecRef ); if ( inData->fInRecRef ) { LockOpenRefTable(); serviceToDeregister = (CFMutableDictionaryRef)::CFDictionaryGetValue( mOpenRefTable, (const void*)inData->fInRecRef ); if ( serviceToDeregister ) CFRetain( serviceToDeregister ); UnlockOpenRefTable(); if ( serviceToDeregister ) { siResult = DeregisterService( inData->fInRecRef, serviceToDeregister ); if ( siResult && siResult != eDSNullAttribute ) { char name[1024] = {0,}; char type[256] = {0,}; CFStringRef nameOfService = (CFStringRef)::CFDictionaryGetValue( serviceToDeregister, kDSNAttrRecordNameSAFE_CFSTR ); if ( nameOfService ) CFStringGetCString( nameOfService, name, sizeof(name), kCFStringEncodingUTF8 ); CFStringRef typeOfService = (CFStringRef)::CFDictionaryGetValue( serviceToDeregister, kDS1AttrServiceTypeSAFE_CFSTR ); if ( !typeOfService ) typeOfService = (CFStringRef)::CFDictionaryGetValue( serviceToDeregister, kDSNAttrRecordTypeSAFE_CFSTR ); if ( typeOfService ) CFStringGetCString( typeOfService, type, sizeof(type), kCFStringEncodingUTF8 ); DBGLOG( "DS (%s) couldn't deregister %s (%s) due to an error: %d!\n", GetProtocolPrefixString(), name, type, siResult ); } CFRelease( serviceToDeregister ); } else syslog( LOG_ERR, "CNSLPlugin::DeleteRecord, couldn't find record to delete!\n" ); } else { siResult = eDSInvalidRecordRef; syslog( LOG_ERR, "CNSLPlugin::DeleteRecord called with invalid fInRecRef (0x%x)!\n", inData->fInRecRef ); } return( siResult ); } // DeleteRecord sInt32 CNSLPlugin::FlushRecord ( sFlushRecord *inData ) { sInt32 siResult = eDSNoErr; CFMutableDictionaryRef serviceToRegister = NULL; DBGLOG( "CNSLPlugin::FlushRecord called on refNum:%ld\n", inData->fInRecRef ); try { if ( inData->fInRecRef ) { LockOpenRefTable(); serviceToRegister = (CFMutableDictionaryRef)::CFDictionaryGetValue( mOpenRefTable, (const void*)inData->fInRecRef ); if ( serviceToRegister ) CFRetain( serviceToRegister ); UnlockOpenRefTable(); if ( serviceToRegister ) { DBGLOG( "CNSLPlugin::FlushRecord calling RegisterService with the following service:\n" ); if ( getenv( "NSLDEBUG" ) ) ::CFShow( serviceToRegister ); siResult = RegisterService( inData->fInRecRef, serviceToRegister ); if ( siResult && siResult != eDSNullAttribute ) { char name[1024] = {0,}; char type[256] = {0,}; CFStringRef nameOfService = (CFStringRef)::CFDictionaryGetValue( serviceToRegister, kDSNAttrRecordNameSAFE_CFSTR ); if ( nameOfService ) CFStringGetCString( nameOfService, name, sizeof(name), kCFStringEncodingUTF8 ); CFStringRef typeOfService = (CFStringRef)::CFDictionaryGetValue( serviceToRegister, kDS1AttrServiceTypeSAFE_CFSTR ); if ( !typeOfService ) typeOfService = (CFStringRef)::CFDictionaryGetValue( serviceToRegister, kDSNAttrRecordTypeSAFE_CFSTR ); if ( typeOfService ) CFStringGetCString( typeOfService, type, sizeof(type), kCFStringEncodingUTF8 ); syslog( LOG_ERR, "DS (%s) couldn't register %s (%s) due to an error: %d!\n", GetProtocolPrefixString(), name, type, siResult ); } CFRelease( serviceToRegister ); } else DBGLOG( "CNSLPlugin::FlushRecord, couldn't find service to register!\n" ); } else { siResult = eDSInvalidReference; DBGLOG( "CNSLPlugin::FlushRecord called with invalid fInRecRef.\n" ); } } catch ( int err ) { siResult = err; } return( siResult ); } // DeleteRecord #pragma mark - sInt32 CNSLPlugin::AddAttributeValue ( sAddAttributeValue *inData ) { sInt32 siResult = eDSNoErr; CFMutableDictionaryRef serviceToManipulate = NULL; DBGLOG( "CNSLPlugin::AddAttributeValue called\n" ); try { if ( inData->fInRecRef ) { LockOpenRefTable(); serviceToManipulate = (CFMutableDictionaryRef)::CFDictionaryGetValue( mOpenRefTable, (const void*)inData->fInRecRef ); if ( serviceToManipulate ) CFRetain( serviceToManipulate ); UnlockOpenRefTable(); if ( serviceToManipulate && CFGetTypeID(serviceToManipulate) == CFDictionaryGetTypeID() ) { CFStringRef keyRef, valueRef; CFTypeRef existingValueRef = NULL; keyRef = ::CFStringCreateWithCString( kCFAllocatorDefault, inData->fInAttrType->fBufferData, kCFStringEncodingUTF8 ); valueRef = ::CFStringCreateWithCString( kCFAllocatorDefault, inData->fInAttrValue->fBufferData, kCFStringEncodingUTF8 ); if ( keyRef && valueRef ) { existingValueRef = ::CFDictionaryGetValue( serviceToManipulate, keyRef ); if ( existingValueRef && ::CFGetTypeID( existingValueRef ) == ::CFArrayGetTypeID() ) { // this key is already represented by an array of values, just append this latest one ::CFArrayAppendValue( (CFMutableArrayRef)existingValueRef, valueRef ); } else if ( existingValueRef && ::CFGetTypeID( existingValueRef ) == ::CFStringGetTypeID() ) { // if this is the service type, then we want to ignore as this was already set... if ( ::CFStringCompare( (CFStringRef)keyRef, kDS1AttrServiceTypeSAFE_CFSTR, 0 ) != kCFCompareEqualTo ) { // is this value the same as what we want to add? If so skip, otherwise make it an array if ( ::CFStringCompare( (CFStringRef)existingValueRef, valueRef, 0 ) != kCFCompareEqualTo ) { // this key was represented by a string, we need to swap it with an new array with the two values CFStringRef oldStringRef = (CFStringRef)existingValueRef; CFMutableArrayRef newArrayRef = ::CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks ); ::CFArrayAppendValue( newArrayRef, oldStringRef ); ::CFArrayAppendValue( newArrayRef, valueRef ); ::CFDictionaryRemoveValue( serviceToManipulate, keyRef ); ::CFDictionaryAddValue( serviceToManipulate, keyRef, newArrayRef ); ::CFRelease( newArrayRef ); } } } else { // nothing already there, we'll just add this string to the dictionary ::CFDictionaryAddValue( serviceToManipulate, keyRef, valueRef ); } ::CFRelease( keyRef ); ::CFRelease( valueRef ); } else if ( !keyRef ) siResult = eDSInvalidRefType; else if ( !valueRef ) siResult = eDSInvalidAttrValueRef; } else siResult = eDSInvalidRecordRef; if ( serviceToManipulate ) CFRelease( serviceToManipulate ); } else { siResult = eDSInvalidRecordRef; DBGLOG( "CNSLPlugin::AddAttributeValue called but with no value in fInRecRef (0x%x) not in our list.\n", (const void*)inData->fInRecRef ); } } catch ( int err ) { siResult = err; } return( siResult ); } sInt32 CNSLPlugin::RemoveAttribute ( sRemoveAttribute *inData ) { sInt32 siResult = eDSNoErr; CFMutableDictionaryRef serviceToManipulate = NULL; try { if ( inData->fInRecRef ) { LockOpenRefTable(); serviceToManipulate = (CFMutableDictionaryRef)::CFDictionaryGetValue( mOpenRefTable, (const void*)inData->fInRecRef ); CFStringRef keyRef; keyRef = ::CFStringCreateWithCString( kCFAllocatorDefault, inData->fInAttribute->fBufferData, kCFStringEncodingUTF8 ); if ( keyRef ) { ::CFDictionaryRemoveValue( serviceToManipulate, keyRef ); ::CFRelease( keyRef ); } UnlockOpenRefTable(); } else { siResult = eDSInvalidRecordRef; DBGLOG( "CNSLPlugin::RemoveAttribute called but with no value in fOurRecRef.\n" ); } } catch ( int err ) { siResult = err; } return( siResult ); } sInt32 CNSLPlugin::RemoveAttributeValue ( sRemoveAttributeValue *inData ) { sInt32 siResult = eDSNoErr; CFMutableDictionaryRef serviceToManipulate = NULL; try { if ( inData->fInRecRef ) { LockOpenRefTable(); serviceToManipulate = (CFMutableDictionaryRef)::CFDictionaryGetValue( mOpenRefTable, (const void*)inData->fInRecRef ); if ( serviceToManipulate ) CFRetain( serviceToManipulate ); UnlockOpenRefTable(); if ( serviceToManipulate ) { CFStringRef keyRef; CFPropertyListRef valueRef; keyRef = ::CFStringCreateWithCString( kCFAllocatorDefault, inData->fInAttrType->fBufferData, kCFStringEncodingUTF8 ); valueRef = (CFPropertyListRef)::CFDictionaryGetValue( serviceToManipulate, keyRef ); if ( valueRef && ::CFGetTypeID( valueRef ) == ::CFArrayGetTypeID() ) { if ( (UInt32)::CFArrayGetCount( (CFMutableArrayRef)valueRef ) > inData->fInAttrValueID ) ::CFArrayRemoveValueAtIndex( (CFMutableArrayRef)valueRef, inData->fInAttrValueID ); else siResult = eDSIndexOutOfRange; } else if ( valueRef && ::CFGetTypeID( valueRef ) == ::CFStringGetTypeID() ) { ::CFDictionaryRemoveValue( serviceToManipulate, keyRef ); } else siResult = eDSInvalidAttrValueRef; if ( keyRef ) ::CFRelease( keyRef ); if ( valueRef ) ::CFRelease( valueRef ); CFRelease( serviceToManipulate ); } } else { siResult = eDSInvalidRecordRef; DBGLOG( "CNSLPlugin::RemoveAttributeValue called but with no value in fOurRecRef.\n" ); } } catch ( int err ) { siResult = err; } return( siResult ); } sInt32 CNSLPlugin::SetAttributeValue ( sSetAttributeValue *inData ) { sInt32 siResult = eDSNoErr; CFMutableDictionaryRef serviceToManipulate = NULL; try { if ( inData->fInRecRef ) { LockOpenRefTable(); serviceToManipulate = (CFMutableDictionaryRef)::CFDictionaryGetValue( mOpenRefTable, (const void*)inData->fInRecRef ); if ( serviceToManipulate ) CFRetain( serviceToManipulate ); UnlockOpenRefTable(); if ( serviceToManipulate ) { CFStringRef keyRef = NULL, valueRef = NULL; CFMutableArrayRef attributeArrayRef = NULL; keyRef = ::CFStringCreateWithCString( kCFAllocatorDefault, inData->fInAttrType->fBufferData, kCFStringEncodingUTF8 ); valueRef = ::CFStringCreateWithCString( kCFAllocatorDefault, inData->fInAttrValueEntry->fAttributeValueData.fBufferData, kCFStringEncodingUTF8 ); if ( keyRef ) attributeArrayRef = (CFMutableArrayRef)::CFDictionaryGetValue( serviceToManipulate, keyRef ); if ( attributeArrayRef && ::CFGetTypeID( attributeArrayRef ) == ::CFArrayGetTypeID() ) { if ( (UInt32)::CFArrayGetCount( (CFMutableArrayRef)attributeArrayRef ) > inData->fInAttrValueEntry->fAttributeValueID ) ::CFArraySetValueAtIndex( (CFMutableArrayRef)attributeArrayRef, inData->fInAttrValueEntry->fAttributeValueID, valueRef ); else siResult = eDSIndexOutOfRange; } else if ( attributeArrayRef && ::CFGetTypeID( attributeArrayRef ) == ::CFStringGetTypeID() ) { ::CFDictionaryRemoveValue( serviceToManipulate, keyRef ); ::CFDictionarySetValue( serviceToManipulate, keyRef, valueRef ); } else siResult = eDSInvalidAttrValueRef; if ( keyRef ) CFRelease( keyRef ); if ( valueRef ) CFRelease( valueRef ); CFRelease( serviceToManipulate ); } } else { siResult = eDSInvalidRecordRef; DBGLOG( "CNSLPlugin::SetAttributeValue called but with no value in fOurRecRef.\n" ); } } catch ( int err ) { siResult = err; } return( siResult ); } sInt32 CNSLPlugin::RegisterService( tRecordReference recordRef, CFDictionaryRef service ) { return eDSReadOnly; } sInt32 CNSLPlugin::DeregisterService( tRecordReference recordRef, CFDictionaryRef service ) { return eDSReadOnly; } #pragma mark - void CNSLPlugin::HandleNetworkTransitionIfTime( void ) { if (time(nil) >= mTransitionCheckTime) HandleNetworkTransition( NULL ); } sInt32 CNSLPlugin::HandleNetworkTransition( sHeader *inData ) { sInt32 siResult = eDSNoErr; DBGLOG( "CNSLPlugin::HandleNetworkTransition called (%s)\n", GetProtocolPrefixString() ); if ( mActivatedByNSL && IsActive() ) { ClearOutAllNodes(); ResetNodeLookupTimer( kNodeTimerIntervalImmediate ); } else { DBGLOG( "CNSLPlugin::HandleNetworkTransition called (%s) but mActivatedByNSL == %d && IsActive() == %d \n", GetProtocolPrefixString(), mActivatedByNSL, IsActive() ); } return siResult; } Boolean CNSLPlugin::IsClientAuthorizedToCreateRecords( sCreateRecord *inData ) { return true; } Boolean CNSLPlugin::ResultMatchesRequestCriteria( const CNSLResult* result, sGetRecordList* request ) { // things that need to match up: // record type ( we're assuming this is fine for now ) // record name ( a particular entry, dsRecordsAll, dsRecordsStandardAll, or dsRecordsNativeAll ) if ( request->fInPatternMatch == eDSAnyMatch ) return true; Boolean resultIsOK = false; CAttributeList* cpRecNameList = new CAttributeList( request->fInRecNameList ); CFStringRef resultRecordNameRef = result->GetAttributeRef( kDSNAttrRecordNameSAFE_CFSTR ); if ( cpRecNameList ) { sInt32 error = eDSNoErr; uInt32 numRecNames = cpRecNameList->GetCount(); char *pRecName = nil; for ( uInt16 i=1; !resultIsOK && i<=numRecNames; i++ ) { if ( (error = cpRecNameList->GetAttribute( i, &pRecName )) == eDSNoErr ) { CFStringRef recNameMatchRef = NULL; if ( pRecName && strcmp( pRecName, kDSRecordsAll ) == 0 ) { resultIsOK = true; break; } else if ( pRecName && strcmp( pRecName, kDSRecordsStandardAll ) == 0 ) { if ( result->GetAttributeRef( kDSNAttrRecordNameSAFE_CFSTR ) && CFStringHasPrefix( result->GetAttributeRef( kDSNAttrRecordTypeSAFE_CFSTR ), kDSStdRecordTypePrefixSAFE_CFSTR ) ) { resultIsOK = true; break; } else continue; } else if ( pRecName && strcmp( pRecName, kDSRecordsNativeAll ) == 0 ) { if ( result->GetAttributeRef( kDSNAttrRecordNameSAFE_CFSTR ) && CFStringHasPrefix( result->GetAttributeRef( kDSNAttrRecordTypeSAFE_CFSTR ), kDSNativeRecordTypePrefixSAFE_CFSTR ) ) { resultIsOK = true; break; } else continue; } recNameMatchRef = CFStringCreateWithCString( NULL, pRecName, kCFStringEncodingUTF8 ); if ( recNameMatchRef ) { if ( request->fInPatternMatch == eDSExact && CFStringCompare( resultRecordNameRef, recNameMatchRef, 0 ) == kCFCompareEqualTo ) { resultIsOK = true; } else if ( request->fInPatternMatch >= eDSiExact && CFStringCompare( resultRecordNameRef, recNameMatchRef, kCFCompareCaseInsensitive ) == kCFCompareEqualTo ) { resultIsOK = true; } else if ( request->fInPatternMatch == eDSStartsWith && CFStringHasPrefix( resultRecordNameRef, recNameMatchRef ) ) { resultIsOK = true; } else if ( request->fInPatternMatch == eDSEndsWith && CFStringHasSuffix( resultRecordNameRef, recNameMatchRef ) ) { resultIsOK = true; } else if ( request->fInPatternMatch == eDSContains ) { CFRange result = CFStringFind( resultRecordNameRef, recNameMatchRef, 0 ); if ( result.length > 0 ) resultIsOK = true; } CFRelease( recNameMatchRef ); } } } delete cpRecNameList; } return resultIsOK; } sInt32 CNSLPlugin::RetrieveResults( sGetRecordList* inData, CNSLDirNodeRep* nodeRep ) { sInt32 siResult = eDSNoErr; CDataBuff* aRecData = nil; CDataBuff* aAttrData = nil; CDataBuff* aTempData = nil; CBuff* outBuff = nil; DBGLOG( "CNSLPlugin::RetrieveResults called for %lx\n", inData->fInNodeRef ); // we only support certain pattern matches which are defined in CNSLPlugin::ResultMatchesRequestCriteria if ( inData->fInPatternMatch != eDSExact && inData->fInPatternMatch != eDSiExact && inData->fInPatternMatch != eDSAnyMatch && inData->fInPatternMatch != eDSStartsWith && inData->fInPatternMatch != eDSEndsWith && inData->fInPatternMatch != eDSContains ) { return eNotYetImplemented; } try { aRecData = new CDataBuff(); if( !aRecData ) throw( eMemoryError ); aAttrData = new CDataBuff(); if( !aAttrData ) throw( eMemoryError ); aTempData = new CDataBuff(); if( !aTempData ) throw( eMemoryError ); // copy the buffer data into a more manageable form outBuff = new CBuff(); if( !outBuff ) throw( eMemoryError ); siResult = outBuff->Initialize( inData->fInDataBuff, true ); if( siResult ) throw ( siResult ); siResult = outBuff->GetBuffStatus(); if( siResult ) throw ( siResult ); siResult = outBuff->SetBuffType( kClientSideParsingBuff ); if( siResult ) throw ( siResult ); while ( nodeRep->HaveResults() && !siResult ) { // package the record into the DS format into the buffer // steps to add an entry record to the buffer const CNSLResult* newResult = nodeRep->GetNextResult(); // ok, so first check to see that this result matches the caller's criteria if ( !ResultMatchesRequestCriteria( newResult, inData ) ) continue; // get next one aAttrData->Clear(); aRecData->Clear(); if ( newResult ) { char stackBuf[1024]; char* curURL = NULL; char* curServiceType = NULL; CFIndex curServiceTypeLength = 0; CFStringRef serviceTypeRef = NULL; char* recordName = NULL; CFIndex recordNameLength = 0; CFStringRef recordNameRef = NULL; serviceTypeRef = newResult->GetServiceTypeRef(); if ( !serviceTypeRef ) continue; // skip this result curServiceTypeLength = ::CFStringGetMaximumSizeForEncoding( CFStringGetLength(serviceTypeRef), kCFStringEncodingUTF8) +1; if ( curServiceTypeLength > (CFIndex)sizeof(stackBuf) ) curServiceType = (char*)malloc( curServiceTypeLength+1 ); else curServiceType = stackBuf; if ( !::CFStringGetCString( serviceTypeRef, curServiceType, curServiceTypeLength+1, kCFStringEncodingUTF8 ) ) { DBGLOG( "CNSLPlugin::RetrieveResults couldn't convert serviceTypeRef CFString to char* in UTF8!\n" ); curServiceType[0] = '\0'; } else DBGLOG( "CNSLPlugin::RetrieveResults curServiceType=%s\n", curServiceType ); char* recType = CreateRecTypeFromURL( curServiceType ); if ( curServiceType != stackBuf ) free( curServiceType ); curServiceType = NULL; if ( recType != nil ) { DBGLOG( "CNSLPlugin::RetrieveResults recType=%s\n", recType ); aRecData->AppendShort( ::strlen( recType ) ); aRecData->AppendString( recType ); free(recType); } // what to do if the recType is nil? - never get here then else { aRecData->AppendShort( ::strlen( "Record Type Unknown" ) ); aRecData->AppendString( "Record Type Unknown" ); } // now the record name recordNameRef = newResult->GetAttributeRef( kDSNAttrRecordNameSAFE_CFSTR ); if ( !recordNameRef ) { continue; // skip this result } recordNameLength = ::CFStringGetMaximumSizeForEncoding( CFStringGetLength(recordNameRef), kCFStringEncodingUTF8) +1; if ( recordNameLength > (CFIndex)sizeof(stackBuf) ) recordName = (char*)malloc( recordNameLength ); else recordName = stackBuf; if ( !::CFStringGetCString( recordNameRef, recordName, recordNameLength, kCFStringEncodingUTF8 ) ) { DBGLOG( "CNSLPlugin::RetrieveResults couldn't convert recordNameRef CFString to char* in UTF8!\n" ); recordName[0] = '\0'; } else DBGLOG( "CNSLPlugin::RetrieveResults recordName=%s\n", recordName ); if ( recordNameRef != nil && recordName[0] != '\0' ) { aRecData->AppendShort( ::strlen( recordName ) ); aRecData->AppendString( recordName ); } else { aRecData->AppendShort( ::strlen( "Record Name Unknown" ) ); aRecData->AppendString( "Record Name Unknown" ); } if ( recordName != stackBuf ) free( recordName ); recordName = NULL; // now the attributes AttrDataContext context = {0,aAttrData,inData->fInAttribInfoOnly}; if ( newResult->GetAttributeDict() ) { // we want to pull out the appropriate attributes that the client is searching for... uInt32 numAttrTypes = 0; CAttributeList cpAttributeList( inData->fInAttribTypeList ); //save the number of rec types here to use in separating the buffer data numAttrTypes = cpAttributeList.GetCount(); if( (numAttrTypes == 0) ) throw( eDSEmptyRecordTypeList ); sInt32 error = eDSNoErr; char* pAttrType = nil; char* valueBuf = NULL; DBGLOG( "CNSLPlugin::RetrieveResults, numAttrTypes:%ld\n", numAttrTypes ); for ( uInt32 i=1; i<=numAttrTypes; i++ ) { if ( (error = cpAttributeList.GetAttribute( i, &pAttrType )) == eDSNoErr ) { DBGLOG( "CNSLPlugin::RetrieveResults, GetAttribute returned pAttrType:%s\n", pAttrType ); if ( strcmp( pAttrType, kDSAttributesAll ) == 0 ) { // we want to return everything ::CFDictionaryApplyFunction( newResult->GetAttributeDict(), AddToAttrData, &context ); } else { char stackBuf[1024]; CFStringRef keyRef = ::CFStringCreateWithCString( NULL, pAttrType, kCFStringEncodingUTF8 ); CFStringRef valueRef = NULL; CFTypeRef valueTypeRef = NULL; CFArrayRef valueArrayRef = NULL; aTempData->Clear(); if ( ::CFDictionaryContainsKey( newResult->GetAttributeDict(), keyRef ) ) valueTypeRef = (CFTypeRef)::CFDictionaryGetValue( newResult->GetAttributeDict(), keyRef ); CFRelease( keyRef ); if ( valueTypeRef ) { if ( CFGetTypeID(valueTypeRef) == CFArrayGetTypeID() ) { // just point our valueArrayRef at this valueArrayRef = (CFArrayRef)valueTypeRef; CFRetain( valueArrayRef ); // so we can release this } else if ( CFGetTypeID(valueTypeRef) == CFStringGetTypeID() ) { valueRef = (CFStringRef)valueTypeRef; valueArrayRef = CFArrayCreateMutable( NULL, 1, &kCFTypeArrayCallBacks ); if ( CFStringGetLength( valueRef ) > 0 ) CFArrayAppendValue( (CFMutableArrayRef)valueArrayRef, valueRef ); } else DBGLOG( "CNSLPlugin::RetrieveResults, got unknown value type (%ld), ignore\n", CFGetTypeID(valueTypeRef) ); if ( valueArrayRef ) { aTempData->AppendShort( ::strlen( pAttrType ) ); // attrTypeLen aTempData->AppendString( pAttrType ); // attrType DBGLOG( "CNSLPlugin::RetrieveResults, adding pAttrType: %s\n", pAttrType ); // Append the attribute value count aTempData->AppendShort( CFArrayGetCount(valueArrayRef) ); // attrValueCnt CFIndex valueArrayCount = CFArrayGetCount(valueArrayRef); for ( CFIndex i=0; ifInAttribInfoOnly && valueRef && ::CFStringGetLength(valueRef) > 0 ) { CFIndex maxValueEncodedSize = ::CFStringGetMaximumSizeForEncoding(CFStringGetLength(valueRef),kCFStringEncodingUTF8) + 1; if ( maxValueEncodedSize > (CFIndex)sizeof(stackBuf) ) valueBuf = (char*)malloc( maxValueEncodedSize ); else valueBuf = stackBuf; if ( ::CFStringGetCString( valueRef, valueBuf, maxValueEncodedSize, kCFStringEncodingUTF8 ) ) { // Append attribute value aTempData->AppendShort( ::strlen( valueBuf ) ); aTempData->AppendString( valueBuf ); DBGLOG( "CNSLPlugin::RetrieveResults, adding valueBuf: %s\n", valueBuf ); } else DBGLOG( "CNSLPlugin::RetrieveResults, CFStringGetCString couldn't create a string for valueRef!\n" ); if ( valueBuf != stackBuf ) delete( valueBuf ); valueBuf = NULL; } } CFRelease( valueArrayRef ); // now release valueArrayRef = NULL; } else if ( valueRef && CFStringGetLength(valueRef) == 0 ) DBGLOG( "CNSLPlugin::RetrieveResults, CFStringGetLength(valueRef) == 0!\n" ); else if ( inData->fInAttribInfoOnly ) DBGLOG( "CNSLPlugin::RetrieveResults, inData->fInAttribInfoOnly\n" ); context.count++; aAttrData->AppendShort(aTempData->GetLength()); aAttrData->AppendBlock(aTempData->GetData(), aTempData->GetLength() ); } } } else { DBGLOG( "CNSLPlugin::RetrieveResults, GetAttribute returned error:%li\n", error ); } } } // Attribute count aRecData->AppendShort( context.count ); // now add the attributes to the record aRecData->AppendBlock( aAttrData->GetData(), aAttrData->GetLength() ); DBGLOG( "CNSLPlugin::RetrieveResults calling outBuff->AddData()\n" ); siResult = outBuff->AddData( aRecData->GetData(), aRecData->GetLength() ); if ( curURL ) free( curURL ); curURL = NULL; if ( siResult == CBuff::kBuffFull ) { DBGLOG( "CNSLPlugin::RetrieveResults siResult == CBuff::kBuffFull, reset last result for next time\n" ); nodeRep->NeedToRedoLastResult(); // couldn't get this one in the buffer UInt32 numDataBlocks = 0; if ( outBuff->GetDataBlockCount( &numDataBlocks ) != eDSNoErr || numDataBlocks == 0 ) siResult = eDSBufferTooSmall; // we couldn't fit any other data in here either } else inData->fOutRecEntryCount++; } } if ( siResult == CBuff::kBuffFull ) siResult = eDSNoErr; outBuff->SetLengthToSize(); } catch ( int err ) { siResult = err; } if (aRecData != nil) { delete (aRecData); aRecData = nil; } if (aTempData != nil) { delete (aTempData); aTempData = nil; } if (aAttrData != nil) { delete (aAttrData); aAttrData = nil; } if ( outBuff != nil ) { delete( outBuff ); outBuff = nil; } return( siResult ); } // --------------------------------------------------------------------------- // * CreateNSLTypeFromRecType // --------------------------------------------------------------------------- char* CNSLPlugin::CreateNSLTypeFromRecType ( char *inRecType ) { char *outResult = nil; uInt32 uiStrLen = 0; uInt32 uiNativeLen = ::strlen( kDSNativeRecordTypePrefix ); uInt32 uiStdLen = ::strlen( kDSStdRecordTypePrefix ); DBGLOG( "CNSLPlugin::CreateNSLTypeFromRecType called on %s\n", inRecType ); //idea here is to use the inIndex to request a specific map //if inIndex is 1 then the first map will be returned //if inIndex is >= 1 and <= totalCount then that map will be returned //if inIndex is <= 0 then nil will be returned //if inIndex is > totalCount nil will be returned //note the inIndex will reference the inIndexth entry ie. start at 1 //caller can increment the inIndex starting at one and get maps until nil is returned if ( ( inRecType != nil ) ) { uiStrLen = ::strlen( inRecType ); // First look for native record type if ( ::strncmp( inRecType, kDSNativeRecordTypePrefix, uiNativeLen ) == 0 ) { DBGLOG( "CNSLPlugin::CreateNSLTypeFromRecType kDSNativeRecordTypePrefix, uiStrLen:%ld uiNativeLen:%ld\n", uiStrLen, uiNativeLen ); // Make sure we have data past the prefix if ( uiStrLen > uiNativeLen ) { uiStrLen = uiStrLen - uiNativeLen; outResult = new char[ uiStrLen + 2 ]; ::strcpy( outResult, inRecType + uiNativeLen ); } }//native maps //now deal with the standard mappings else if ( ::strncmp( inRecType, kDSStdRecordTypePrefix, uiStdLen ) == 0 ) { DBGLOG( "CNSLPlugin::CreateNSLTypeFromRecType kDSStdRecordTypePrefix, uiStrLen:%ld uiStdLen:%ld\n", uiStrLen, uiStdLen ); if ( strcmp( inRecType, kDSStdRecordTypeAFPServer ) == 0 ) { outResult = new char[1+::strlen(kAFPServiceType)]; ::strcpy(outResult, kAFPServiceType); } else if ( strcmp( inRecType, kDSStdRecordTypeSMBServer ) == 0 ) { outResult = new char[1+::strlen(kSMBServiceType)]; ::strcpy(outResult, kSMBServiceType); } else if ( strcmp( inRecType, kDSStdRecordTypeFTPServer ) == 0 ) { outResult = new char[1+::strlen(kFTPServiceType)]; ::strcpy(outResult, kFTPServiceType); } else if ( strcmp( inRecType, kDSStdRecordTypeNFS ) == 0 ) { outResult = new char[1+::strlen(kNFSServiceType)]; ::strcpy(outResult, kNFSServiceType); } else if ( strcmp( inRecType, kDSStdRecordTypeWebServer ) == 0 ) { outResult = new char[1+::strlen(kHTTPServiceType)]; ::strcpy(outResult, kHTTPServiceType); } else if ( strcmp( inRecType, kDSStdRecordTypePrinters ) == 0 ) { outResult = new char[1+::strlen(kLaserWriterServiceType)]; ::strcpy(outResult, kLaserWriterServiceType); } else if ( uiStrLen > uiStdLen ) { // don't return a result for standard types we don't support /* uiStrLen = uiStrLen - uiStdLen; outResult = new char[ uiStrLen + 2 ]; ::strcpy( outResult, inRecType + uiStdLen ); */ } else DBGLOG( "CNSLPlugin::CreateNSLTypeFromRecType, uiStrLen:%ld <= uiStdLen:%ld\n", uiStrLen, uiStdLen ); }//standard maps else DBGLOG( "CNSLPlugin::CreateNSLTypeFromRecType, inRecType:%s doesn't map to \"dsRecTypeNative:\" OR \"dsRecTypeStandard:\"!\n", inRecType ); }// ( inRecType != nil ) DBGLOG( "CNSLPlugin::CreateNSLTypeFromRecType mapping %s to %s\n", inRecType, outResult ); return( outResult ); } // CreateNSLTypeFromRecType // --------------------------------------------------------------------------- // * CreateRecTypeFromURL // --------------------------------------------------------------------------- char* CNSLPlugin::CreateRecTypeFromURL( char *inNSLType ) { char *outResult = nil; uInt32 uiStrLen = 0; DBGLOG( "CNSLPlugin::CreateRecTypeFromURL called to map %s to a DS type\n", inNSLType ); //idea here is to use the inIndex to request a specific map //if inIndex is 1 then the first map will be returned //if inIndex is >= 1 and <= totalCount then that map will be returned //if inIndex is <= 0 then nil will be returned //if inIndex is > totalCount nil will be returned //note the inIndex will reference the inIndexth entry ie. start at 1 //caller can increment the inIndex starting at one and get maps until nil is returned if ( inNSLType != nil ) { uiStrLen = ::strlen( inNSLType ); if ( strcmp( inNSLType, kAFPServiceType ) == 0 ) { outResult = new char[1+::strlen(kDSStdRecordTypeAFPServer)]; ::strcpy(outResult,kDSStdRecordTypeAFPServer); } else if ( strcmp( inNSLType, kSMBServiceType ) == 0 ) { outResult = new char[1+::strlen(kDSStdRecordTypeSMBServer)]; ::strcpy(outResult,kDSStdRecordTypeSMBServer); } else if ( strcmp( inNSLType, kNFSServiceType ) == 0 ) { outResult = new char[1+::strlen(kDSStdRecordTypeNFS)]; ::strcpy(outResult,kDSStdRecordTypeNFS); } else if ( strcmp( inNSLType, kFTPServiceType ) == 0 ) { outResult = new char[1+::strlen(kDSStdRecordTypeFTPServer)]; ::strcpy(outResult,kDSStdRecordTypeFTPServer); } else if ( strcmp( inNSLType, kHTTPServiceType ) == 0 ) { outResult = new char[1+::strlen(kDSStdRecordTypeWebServer)]; ::strcpy(outResult,kDSStdRecordTypeWebServer); } else { // native record type outResult = new char[1+::strlen(kDSNativeAttrTypePrefix)+strlen(inNSLType)]; ::strcpy(outResult,kDSNativeAttrTypePrefix); ::strcat(outResult,inNSLType); } }// ( inNSLType != nil ) return( outResult ); } // CreateRecTypeFromURL CFStringRef CNSLPlugin::CreateRecTypeFromNativeType ( char *inNativeType ) { CFMutableStringRef outResultRef = NULL; DBGLOG( "CNSLPlugin::CreateRecTypeFromNativeType called on %s\n", inNativeType ); if ( ( inNativeType != nil ) ) { outResultRef = CFStringCreateMutable( NULL, 0 ); if ( ::strcmp( inNativeType, kAFPServiceType ) == 0 ) { CFStringAppend( outResultRef, kDSStdRecordTypeAFPServerSAFE_CFSTR ); } else if ( ::strcmp( inNativeType, kSMBServiceType ) == 0 ) { CFStringAppend( outResultRef, kDSStdRecordTypeSMBServerSAFE_CFSTR ); } else if ( ::strcmp( inNativeType, kNFSServiceType ) == 0 ) { CFStringAppend( outResultRef, kDSStdRecordTypeNFSSAFE_CFSTR ); } else if ( ::strcmp( inNativeType, kDSStdRecordTypeFTPServer ) == 0 ) { CFStringAppend( outResultRef, kDSStdRecordTypeFTPServerSAFE_CFSTR ); } else if ( ::strcmp( inNativeType, kDSStdRecordTypeWebServer ) == 0 ) { CFStringAppend( outResultRef, kDSStdRecordTypeWebServerSAFE_CFSTR ); } else if ( outResultRef ) { CFStringAppend( outResultRef, kDSNativeRecordTypePrefixSAFE_CFSTR ); CFStringRef nativeStringRef = CFStringCreateWithCString( NULL, inNativeType, kCFStringEncodingUTF8 ); if ( nativeStringRef ) { CFStringAppend( outResultRef, nativeStringRef ); CFRelease( nativeStringRef ); } } } return( outResultRef ); } // CreateNSLTypeFromRecType CFStringRef CNSLPlugin::CreateRecTypeFromNativeType ( CFStringRef inNativeType ) { CFStringRef returnTypeRef = NULL; UInt32 nativeCStrLen = ::CFStringGetMaximumSizeForEncoding( CFStringGetLength(inNativeType), kCFStringEncodingUTF8 ) + 1; char* nativeCStr = (char*)malloc( nativeCStrLen ); ::CFStringGetCString( inNativeType, nativeCStr, nativeCStrLen, kCFStringEncodingUTF8 ); returnTypeRef = CreateRecTypeFromNativeType( nativeCStr ); free( nativeCStr ); return returnTypeRef; } // CreateNSLTypeFromRecType #pragma mark - void CNSLPlugin::StartNodeLookup( void ) { mLastNodeLookupStartTime = GetCurrentTime(); // get current time DBGLOG( "CNSLPlugin::StartNodeLookup (%s), called\n", GetProtocolPrefixString() ); ResetNodeLookupTimer( GetTimeBetweenNodeLookups() ); // This is how long before we fire another node lookup (barring network events) NewNodeLookup(); } sInt16 CNSLPlugin::NumOutstandingSearches( void ) { return gOutstandingSearches; } Boolean CNSLPlugin::OKToStartNewSearch( void ) { pthread_mutex_lock(&gOutstandingSearchesLock); Boolean okToStartNewSearch = NumOutstandingSearches() < kMaxNumOutstandingSearches; pthread_mutex_unlock(&gOutstandingSearchesLock); if ( okToStartNewSearch ) DBGLOG( "CNSLPlugin::OKToStartNewSearch is returning true (%d current searches)\n", gOutstandingSearches ); else DBGLOG( "CNSLPlugin::OKToStartNewSearch is returning false (%d current searches)\n", gOutstandingSearches ); return okToStartNewSearch; } void CNSLPlugin::StartSubNodeLookup( char* parentNodeName ) { DBGLOG( "CNSLPlugin::StartSubNodeLookup called on %s\n", parentNodeName ); OKToOpenUnPublishedNode( parentNodeName ); } void CNSLPlugin::ClearOutStaleNodes( void ) { DBGLOG( "CNSLPlugin::ClearOutStaleNodes\n" ); // we want to look at each node we have registered and unregister any whose timestamp is older than // the start of our last node lookup search NSLNodeHandlerContext context = {mPublishedNodes, kClearOutStaleNodes, (void*)GetLastNodeLookupStartTime(), NULL}; CFIndex arrayCount; CFStringRef skeetString; LockPublishedNodes(); ::CFDictionaryApplyFunction( mPublishedNodes, NSLNodeHandlerFunction, &context ); if ( context.fNodesToRemove ) { arrayCount = CFArrayGetCount( context.fNodesToRemove ); for ( CFIndex i = 0; i < arrayCount; i++ ) { skeetString = (CFStringRef)CFArrayGetValueAtIndex( context.fNodesToRemove, i ); if ( skeetString != NULL ) CFDictionaryRemoveValue( mPublishedNodes, skeetString ); } CFRelease( context.fNodesToRemove ); } UnlockPublishedNodes(); } void CNSLPlugin::ClearOutAllNodes( void ) { DBGLOG( "CNSLPlugin::ClearOutAllNodes\n" ); LockPublishedNodes(); ::CFDictionaryRemoveAllValues( mPublishedNodes ); UnlockPublishedNodes(); } void CNSLPlugin::QueueNewSearch( CNSLServiceLookupThread* newLookup ) { LockSearchQueue(); if ( mSearchQueue ) { if ( !mSearchTicklerInstalled ) InstallSearchTickler(); ::CFArrayAppendValue( mSearchQueue, newLookup ); DBGLOG( "CNSLPlugin::QueueNewSearch called, %ld searches waiting to start\n", CFArrayGetCount(mSearchQueue) ); } UnlockSearchQueue(); } #define DEQUEUE_LIFO 1 void CNSLPlugin::StartNextQueuedSearch( void ) { if ( mSearchQueue && ::CFArrayGetCount(mSearchQueue) > 0 ) { #if DEQUEUE_LIFO CNSLServiceLookupThread* newLookup = (CNSLServiceLookupThread*)::CFArrayGetValueAtIndex( mSearchQueue, CFArrayGetCount(mSearchQueue)-1 ); ::CFArrayRemoveValueAtIndex( mSearchQueue, CFArrayGetCount(mSearchQueue)-1 ); #else CNSLServiceLookupThread* newLookup = (CNSLServiceLookupThread*)::CFArrayGetValueAtIndex( mSearchQueue, 0 ); ::CFArrayRemoveValueAtIndex( mSearchQueue, 0 ); #endif LockOpenRefTable(); Boolean newLookupInRefTable = ::CFDictionaryContainsValue( mOpenRefTable, newLookup->GetNodeToSearch() ); UnlockOpenRefTable(); if ( newLookupInRefTable && !newLookup->AreWeCanceled() ) { DBGLOG( "CNSLPlugin::StartNextQueuedSearch starting new search, %ld searches waiting to start\n", CFArrayGetCount(mSearchQueue) ); newLookup->Resume(); } else { DBGLOG( "CNSLPlugin::StartNextQueuedSearch deleting already canceled search, %ld searches waiting to start\n", CFArrayGetCount(mSearchQueue) ); delete newLookup; } } } void CNSLPlugin::StartServicesLookup( char* serviceType, CNSLDirNodeRep* nodeDirRep ) { if ( serviceType ) NewServiceLookup( serviceType, nodeDirRep ); } #pragma mark - void NSLReleaseNodeData( CFAllocatorRef allocator, const void* value ) { NodeData* nodeData = (NodeData*)value; if ( nodeData ) { if ( nodeData->fDSName ) DSUnregisterNode( nodeData->fSignature, nodeData->fDSName ); DeallocateNodeData( nodeData ); } } CFStringRef NSLNodeValueCopyDesctriptionCallback ( const void *value ) { NodeData* nodeData = (NodeData*)value; return nodeData->fNodeName; } Boolean NSLNodeValueEqualCallback ( const void *value1, const void *value2 ) { NodeData* nodeData1 = (NodeData*)value1; NodeData* nodeData2 = (NodeData*)value2; Boolean areEqual = false; if ( nodeData1 && nodeData2 && nodeData1->fNodeName && nodeData2->fNodeName ) areEqual = ( CFStringCompare( nodeData1->fNodeName, nodeData2->fNodeName, kCFCompareCaseInsensitive ) == kCFCompareEqualTo ); return areEqual; } void NSLNodeHandlerFunction(const void *inKey, const void *inValue, void *inContext) { NodeData* curNodeData = (NodeData*)inValue; NSLNodeHandlerContext* context = (NSLNodeHandlerContext*)inContext; if ( !inKey || !inValue || !inContext ) return; switch ( context->fMessage ) { case kClearOutStaleNodes: { if ( curNodeData->fTimeStamp < (UInt32)(context->fDataPtr) ) { // we need to delete this... DBGLOG( "NSLNodeHandlerFunction, Removing Node from published list\n" ); // DSUnregisterNode( curNodeData->fSignature, curNodeData->fDSName ); if ( !context->fNodesToRemove ) context->fNodesToRemove = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks ); if ( curNodeData->fNodeName != NULL ) CFArrayAppendValue( context->fNodesToRemove, curNodeData->fNodeName ); // can't manipulate dictionary while iterating } } break; default: break; }; } #pragma mark - UInt32 GetCurrentTime( void ) // in seconds { struct timeval curTime; if ( gettimeofday( &curTime, NULL ) != 0 ) fprintf( stderr, "call to gettimeofday returned error: %s", strerror(errno) ); return curTime.tv_sec; } void AddToAttrData(const void *key, const void *value, void *context) { CFStringRef keyRef = (CFStringRef)key; CFTypeRef valueTypeRef = (CFTypeRef)value; // need to update this to support an array of values instead CFStringRef valueRef = NULL; CFArrayRef valueArrayRef = NULL; char keyBuf[256] = {0}; char stackBuf[1024]; char* valueBuf = NULL; CDataBuff* aTmpData = nil; AttrDataContext* dataContext = (AttrDataContext*)context; dataContext->count++; // adding one more aTmpData = new CDataBuff(); if( !aTmpData ) throw( eMemoryError ); aTmpData->Clear(); if ( valueTypeRef ) { if ( CFGetTypeID(valueTypeRef) == CFArrayGetTypeID() ) { // just point our valueArrayRef at this valueArrayRef = (CFArrayRef)valueTypeRef; CFRetain( valueArrayRef ); // so we can release this } else if ( CFGetTypeID(valueTypeRef) == CFStringGetTypeID() ) { valueRef = (CFStringRef)valueTypeRef; valueArrayRef = CFArrayCreateMutable( NULL, 1, &kCFTypeArrayCallBacks ); if ( CFStringGetLength( valueRef ) > 0 ) CFArrayAppendValue( (CFMutableArrayRef)valueArrayRef, valueRef ); } else DBGLOG( "AddToAttrData, got unknown value type (%ld), ignore\n", CFGetTypeID(valueTypeRef) ); if ( valueArrayRef && ::CFStringGetCString( keyRef, keyBuf, sizeof(keyBuf), kCFStringEncodingUTF8 ) ) { aTmpData->AppendShort( ::strlen( keyBuf ) ); // attrTypeLen aTmpData->AppendString( keyBuf ); // attrType DBGLOG( "AddToAttrData, adding keyBuf: %s\n", keyBuf ); // Append the attribute value count aTmpData->AppendShort( CFArrayGetCount(valueArrayRef) ); // attrValueCnt CFIndex valueArrayCount = CFArrayGetCount(valueArrayRef); for ( CFIndex i=0; iattrOnly && ::CFStringGetLength(valueRef) > 0 ) { CFIndex maxValueEncodedSize = ::CFStringGetMaximumSizeForEncoding(CFStringGetLength(valueRef), kCFStringEncodingUTF8) + 1; if ( maxValueEncodedSize > (CFIndex)sizeof(stackBuf) ) valueBuf = (char*)malloc( maxValueEncodedSize ); else valueBuf = stackBuf; if ( ::CFStringGetCString( valueRef, valueBuf, maxValueEncodedSize, kCFStringEncodingUTF8 ) ) { // Append attribute value aTmpData->AppendShort( ::strlen( valueBuf ) ); // valueLen aTmpData->AppendString( valueBuf ); // value DBGLOG( "AddToAttrData, adding valueBuf: %s\n", valueBuf ); } else { DBGLOG( "AddToAttrData, CFStringGetCString couldn't create a string for valueRef!\n" ); if ( getenv( "NSLDEBUG" ) ) ::CFShow( valueRef ); } if ( valueBuf != stackBuf ) delete( valueBuf ); valueBuf = NULL; } } // Add the attribute length dataContext->attrDataBuf->AppendShort( aTmpData->GetLength() ); dataContext->attrDataBuf->AppendBlock( aTmpData->GetData(), aTmpData->GetLength() ); aTmpData->Clear(); delete aTmpData; } if ( valueArrayRef ) CFRelease( valueArrayRef ); } } void AddToAttrData2(CFStringRef keyRef, CFStringRef valueRef, void *context) { } /************** * LogHexDump * ************** Log a hex and string formatted version of the given buffer. */ #define kOffset 0 #define kHexData 7 #define kASCIIData (kHexData+50) #define kEOL (kASCIIData+16) #define kLineLength (kEOL+1) #define kLineEnding '\n' #define kBufferTooBig -1 #define kMaxBufSize 10240 #define kMaxPacketLen (((kMaxBufSize - 2)/kLineLength)*16)-15 int LogHexDump(char *pktPtr, long pktLen) { static char hexChars[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; register unsigned char *curPtr=NULL; // pointer into the raw hex buffer register char *hexPtr=NULL; // pointer in line to put next hex output register char *charPtr=NULL; // place to put next character output char *offsetPtr=NULL; // pointer to the offset unsigned char *p=NULL; // indexes through the "offset" short i,j; char *buf=NULL; long bufLen; short numLines; short numCharsOnLine; short remainder; short offset; // offset within raw packet if ( pktLen == 0 ) { DBGLOG( "LogHexDump was passed in a pktLen of zero!\n" ); return noErr; // no data to log } if ( pktLen > kMaxPacketLen ) pktLen = kMaxPacketLen; numLines = (pktLen + 15)/16; // total number of lines (one may be short) remainder = pktLen - (numLines-1)*16; // number of chars on the last line bufLen = numLines * kLineLength + 2; // size of the buffer required if (bufLen > kMaxBufSize) { DBGLOG( "LogHexDump kBufferTooBig (%ld), setting to max log size (%d)\n", bufLen, kMaxBufSize ); bufLen = kMaxBufSize; } DBGLOG( "Dumping %ld bytes of hex-format data:\n",pktLen ); buf = (char*)malloc(bufLen); // (+2 to hold the NUL and final newline) if (!buf) { DBGLOG( "LogHexDump return eMemoryAllocError\n" ); return(eMemoryAllocError); } for (i=0; i>4)]; *(offsetPtr++) = hexChars[(*p++&0x0F)]; // format will be 0000: xx... *(offsetPtr++) = hexChars[(*p>>4)]; *(offsetPtr++) = hexChars[(*p&0x0F)]; *offsetPtr = ':'; if (j == numLines-1) // last line may be short numCharsOnLine = remainder; else numCharsOnLine = 16; for (i=0; i>4)]; *(hexPtr++) = hexChars[(*curPtr&0x0F)]; hexPtr++; // now print the actual printable character, if we can if (isprint(*curPtr)) *(charPtr++) = *curPtr; else *(charPtr++) = '.'; // for unprintables curPtr++; // bump the raw-hex data pointer } *(charPtr++) = kLineEnding; // add in the carriage return at end of each line offsetPtr = charPtr; // and move to the next line in the dest buffer } *(charPtr++) = kLineEnding; *charPtr = '\0'; // and a terminating NULL DBGLOG( "%s\n",buf); free(buf); return noErr; } CFStringEncoding gsEncoding = kCFStringEncodingInvalidId; CFStringEncoding NSLGetSystemEncoding( void ) { if ( gsEncoding == kCFStringEncodingInvalidId ) { // need to parse out the encoding from /var/root/.CFUserTextEncoding FILE * fp; char buf[1024]; CFStringEncoding encoding = 0; fp = fopen("/var/root/.CFUserTextEncoding","r"); if (fp == NULL) { DBGLOG( "NSLGetSystemEncoding: Could not open config file, return 0 (MacRoman)" ); return 0; } if ( fgets(buf,sizeof(buf),fp) != NULL) { int i = 0; while ( buf[i] != '\0' && buf[i] != ':' ) i++; buf[i] = '\0'; char* endPtr = NULL; encoding = strtol(buf,&endPtr,10); } fclose(fp); gsEncoding = encoding; } DBGLOG( "NSLGetSystemEncoding: returning encoding (%ld)", gsEncoding ); return gsEncoding; }