NSLUtil.m   [plain text]


/*
 *  NSLUtil.c
 *  automount
 *
 *  Created by Pat Dirks on Wed Mar 27 2002.
 *  Copyright (c) 2002 __MyCompanyName__. All rights reserved.
 *
 */

#import "log.h"
#include "NSLUtil.h"
#import "automount.h"

#define TRACE_NSL 0

pthread_mutexattr_t gDefaultMutexAttr = { 0 };
pthread_condattr_t gDefaultCondAttr = { 0 };

pascal void XNeighborhoodLookupNotifyProc(void *clientContext, NSLRequestRef requestRef);
pascal void XServicesLookupNotifyProc(void *clientContext, NSLRequestRef requestRef);

/***********************************************************************************
*
*   N S L   U T I L I T Y   F U N C T I O N S
*
***********************************************************************************/

void InitSearchResultList(struct SearchResultList *resultsptr, NSLNeighborhood targetneighborhood) {
    resultsptr->searchTarget = targetneighborhood;
	pthread_mutex_init(&resultsptr->resultListMutex, NULL);
    TAILQ_INIT(&resultsptr->contentsFound);
    pthread_cond_init(&resultsptr->searchResultsCond, NULL);
    resultsptr->searchComplete = 0;
}


void MarkSearchComplete(SearchContextPtr searchContext) {
    searchContext->results->searchComplete = true;
    pthread_cond_broadcast(&searchContext->results->searchResultsCond);
}



void WaitForSearchCompletion(struct SearchResultList *searchResults) {
    while ( !searchResults->searchComplete ) {
        pthread_cond_wait(&searchResults->searchResultsCond, &searchResults->resultListMutex);
        if (!searchResults->searchComplete) pthread_mutex_unlock(&searchResults->resultListMutex);
    };
}



int StartSearchForNeighborhoodsInNeighborhood( NSLNeighborhood ParentNeighborhood, SearchContextPtr callContext )
{
    NSLXClientNotifyUPP		myXNeighborhoodNotifyUPP = NULL;
	NSLError 				iErr = kNSLErrorNoErr;
    
	myXNeighborhoodNotifyUPP = (NSLXClientNotifyUPP) NewNSLXClientNotifyUPP(XNeighborhoodLookupNotifyProc);
	
    // get the default neighborhoods (top level)
    iErr = NSLXStartNeighborhoodLookup( callContext->searchClientRef,
                                        myXNeighborhoodNotifyUPP,
                                        callContext,
                                        ParentNeighborhood,
                                        &callContext->searchRef);
    
    if ( iErr.theErr )
    {
        // all errors returned immediately from NSLXStartNeighborhoodLookup are fatal.
        // non-fatal errors should be reported through the callbacks.
		sys_msg(debug, LOG_ERR, "NSLXStartNeighborhoodLookup returned error: %ld", iErr.theErr);
    };
	
    return iErr.theErr;
}



pascal void XNeighborhoodLookupNotifyProc(void *clientContext, NSLRequestRef requestRef)
{
	NSLNeighborhood	theNeighborhood = NSLXGetNeighborhoodResult(requestRef);
	NSLSearchState	theSearchState = NSLXGetSearchState(requestRef);
	NSLError		theSearchStatus = NSLXGetSearchStatus(requestRef);
	SearchContextPtr callContext = (SearchContextPtr)clientContext;
	struct SearchResult	*thisResult;

    // if we got an empty notification, ignore it
    if ( theSearchStatus.theErr == noErr &&					// no errors
         theNeighborhood == NULL &&							// no items
         theSearchState == kNSLSearchStateOnGoing			// doesn't terminate a search
       ) {
        return;
    } else if (theNeighborhood) {
        // handle the result.  WARNING - this callback can be called on a different pthread than was started on...
        // we'll add a new call, NSLXGetNameFromNeighborhood that returns a CFStringRef...
        char*			name = NULL;
        long			nameLen = 0;
        
        NSLGetNameFromNeighborhood( theNeighborhood, &name, &nameLen );

        if ( name && nameLen > 0 )
        {
			thisResult = (struct SearchResult *)calloc(1, sizeof(struct SearchResult));
			if (thisResult) {
                INIT_SEARCHRESULT(thisResult,
                                  callContext->searchClientRef,
                                  kNetworkNeighborhood,
                                  callContext->results);
				thisResult->result.neighborhood = NSLCopyNeighborhood( theNeighborhood );
                pthread_mutex_lock(&callContext->results->resultListMutex);
				TAILQ_INSERT_TAIL(&callContext->results->contentsFound, thisResult, sibling_link);
                pthread_mutex_unlock(&callContext->results->resultListMutex);
                pthread_cond_signal(&callContext->results->searchResultsCond);
			};
        }
    }
    
    if ( theSearchState == kNSLSearchStateComplete ) {
        MarkSearchComplete(callContext);
	};
}



int StartSearchForServicesInNeighborhood(NSLNeighborhood neighborhood, CFArrayRef serviceTypes, SearchContextPtr callContext )
{
    // start a search in a given neigborhood...
    NSLXClientNotifyUPP		myXServicesNotifyUPP = NULL;
	NSLError 				iErr = kNSLErrorNoErr;
    
    if ( myXServicesNotifyUPP == NULL ) myXServicesNotifyUPP = (NSLXClientNotifyUPP) NewNSLXClientNotifyUPP(XServicesLookupNotifyProc);
    
#if TRACE_NSL
	sys_msg(debug, LOG_DEBUG, "StartSearchForServicesInNeighborhood: Search result list at 0x%08lx = { 0x%08lx, 0x%08lx }.",
								(unsigned long)&(callContext->results->contentsFound),
								(unsigned long)callContext->results->contentsFound.tqh_first,
								(unsigned long)callContext->results->contentsFound.tqh_last);
#endif
	
	iErr = NSLXStartServicesLookup( callContext->searchClientRef,
									myXServicesNotifyUPP,
									callContext,
									neighborhood,
									serviceTypes,
									&callContext->searchRef);

	if ( iErr.theErr )
	{
		// all errors returned immediately from NSLXStartNeighborhoodLookup are fatal.
		// non-fatal errors should be reported through the callbacks.
		sys_msg(debug, LOG_ERR, "NSLXStartNeighborhoodLookup returned error: %ld", iErr.theErr);
	}

    return iErr.theErr;
}



pascal void XServicesLookupNotifyProc(void *clientContext, NSLRequestRef requestRef)
{
	SearchContextPtr callContext = (SearchContextPtr)clientContext;
    NSLService theResult = NSLXGetSearchResult( requestRef );
	NSLSearchState theSearchState = NSLXGetSearchState(requestRef);
	NSLError theSearchStatus = NSLXGetSearchStatus(requestRef);
    struct SearchResult	*thisResult;
    
#if TRACE_NSL
	sys_msg(debug, LOG_DEBUG, "Entering XServicesLookupNotifyProc:");
	sys_msg(debug, LOG_DEBUG, "\ttheSearchStatus.theErr = 0x%08lx", theSearchStatus.theErr);
	sys_msg(debug, LOG_DEBUG, "\ttheResult = 0x%08lx", (unsigned long)theResult);
	sys_msg(debug, LOG_DEBUG, "\ttheSearchState = %d", theSearchState);
#endif
	
    // if we got an empty notification, don't queue it
    if ( (theSearchStatus.theErr == noErr) &&				// no errors
         (theResult == NULL) &&								// no items
         (theSearchState == kNSLSearchStateOnGoing)			// doesn't terminate a search
       )
    {
        return;
    }
    
    if (theResult) 
    {
        thisResult = (struct SearchResult *)calloc(1, sizeof(struct SearchResult));
        if (thisResult) {
#if TRACE_NSL
			sys_msg(debug, LOG_DEBUG, "XServicesLookupNotifyProc: Search result list at 0x%08lx = { 0x%08lx, 0x%08lx }.",
								(unsigned long)&(callContext->results->contentsFound),
								(unsigned long)callContext->results->contentsFound.tqh_first,
								(unsigned long)callContext->results->contentsFound.tqh_last);
			sys_msg(debug, LOG_DEBUG, "\tAdding search result at 0x%08lx...", (unsigned long)thisResult);
#endif
            INIT_SEARCHRESULT(thisResult,
                                callContext->searchClientRef,
                                kNetworkServer,
                                callContext->results);
			thisResult->result.service =
					(NSLService)CFPropertyListCreateDeepCopy( kCFAllocatorDefault,
																			(CFMutableDictionaryRef)theResult,
																			kCFPropertyListMutableContainers);
            pthread_mutex_lock(&callContext->results->resultListMutex);
            TAILQ_INSERT_TAIL(&callContext->results->contentsFound, thisResult, sibling_link);
#if TRACE_NSL
			sys_msg(debug, LOG_DEBUG, "XServicesLookupNotifyProc: Search result list at 0x%08lx = { 0x%08lx, 0x%08lx }.",
								(unsigned long)&(callContext->results->contentsFound),
								(unsigned long)callContext->results->contentsFound.tqh_first,
								(unsigned long)callContext->results->contentsFound.tqh_last);
#endif
            pthread_mutex_unlock(&callContext->results->resultListMutex);
            pthread_cond_signal(&callContext->results->searchResultsCond);
        };
    }

    if ( theSearchState == kNSLSearchStateComplete ) {
        MarkSearchComplete(callContext);
	};
}



//-----------------------------------------------------------------------------------------------
//	GetMainStringFromAttribute
//
//	Returns: The value in the dictionary if it is a CFStringRef, otherwise it tries to find the 
//			 first string in another type (CFArrayRef, CFDictionaryRef).
//-----------------------------------------------------------------------------------------------

CFStringRef GetMainStringFromAttribute( CFDictionaryRef inDict, CFStringRef inKey )
{
    CFStringRef result = NULL;
    CFTypeRef dValue;
    
    if ( CFDictionaryGetValueIfPresent( inDict, inKey, &dValue ) && dValue )
    {
        if ( CFGetTypeID(dValue) == CFStringGetTypeID() )
        {
            result = (CFStringRef)dValue;
        }
        else
        if ( CFGetTypeID(dValue) == CFArrayGetTypeID() )
        {
            result = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)dValue, 0);
        }
    }
    
    return result;
}