/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2008 Apple 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@
 *
 */
/*
    File:       QTAccessFile.cpp

    Contains:   This file contains the implementation for finding and parsing qtaccess files.
                

*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "SafeStdLib.h"
#include "QTSS.h"
#include "OSHeaders.h"
#include "StrPtrLen.h"
#include "StringParser.h"
#include "QTSSModuleUtils.h"
#include "OSFileSource.h"
#include "OSMemory.h"
#include "OSHeaders.h"
#include "QTAccessFile.h"
#include "OSArrayObjectDeleter.h"


#include <grp.h>
#if __MacOSX__
    #include <membership.h>
#endif
#include <pwd.h>
#include <signal.h>
#include <unistd.h>
     
#define DEBUG_QTACCESS 0
#define debug_printf if (DEBUG_QTACCESS) qtss_printf

char* QTAccessFile::sAccessValidUser = "require valid-user\n";
char* QTAccessFile::sAccessAnyUser = "require any-user\n";

UInt8 QTAccessFile::sWhitespaceAndGreaterThanMask[] =
{
    0, 0, 0, 0, 0, 0, 0, 0, 0, 1, //0-9     // \t is a stop
    1, 1, 1, 1, 0, 0, 0, 0, 0, 0, //10-19    //'\r' & '\n' are stop conditions
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20-29
    0, 0, 1, 0, 0, 0, 0, 0, 0, 0, //30-39   ' '  is a stop
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //40-49
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //50-59
    0, 0, 1, 0, 0, 0, 0, 0, 0, 0, //60-69  // '>' is a stop
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //70-79  
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //80-89
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //90-99
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //100-109
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //110-119
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //120-129
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //130-139
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //140-149
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //150-159
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //160-169
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //170-179
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //180-189
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //190-199
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //200-209
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //210-219
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //220-229
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //230-239
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //240-249
    0, 0, 0, 0, 0, 0             //250-255
};

char*       QTAccessFile::sQTAccessFileName = "qtaccess";
Bool16      QTAccessFile::sAllocatedName = false;
OSMutex*    QTAccessFile::sAccessFileMutex = NULL;//QTAccessFile isn't reentrant
const int kBuffLen = 512;

void QTAccessFile::Initialize() // called by server at initialize never call again
{
    if (NULL == sAccessFileMutex)
    {   sAccessFileMutex = NEW OSMutex();
    }
}

void QTAccessFile::SetAccessFileName(const char *inQTAccessFileName)
{
    OSMutexLocker locker(sAccessFileMutex);
    if (NULL == inQTAccessFileName)
    {   Assert(NULL != inQTAccessFileName);
        return;
    }
    
    if (sAllocatedName)
    {   delete [] sQTAccessFileName;
    }
    
    sAllocatedName = true;
    sQTAccessFileName = NEW char[strlen(inQTAccessFileName)+1];
    ::strcpy(sQTAccessFileName, inQTAccessFileName);
    
}


Bool16 QTAccessFile::HaveUser(char *userName, void* extraDataPtr)
{
    Bool16 result = false;

    if (NULL != userName && 0 != userName[0])
        result = true;

   return result;        
}

Bool16 QTAccessFile::HaveGroups( char** groupArray, UInt32 numGroups, void* extraDataPtr)
{
   Bool16 result = false;

   if (numGroups > 0 && groupArray != NULL)
        result = true;
        
   return result;   
}

Bool16 QTAccessFile::HaveRealm(   char *userName, StrPtrLen* ioRealmNameStr, void *extraData )
{
    Bool16 result = false;

    if (ioRealmNameStr != NULL && ioRealmNameStr->Ptr != NULL && ioRealmNameStr->Len > 0)
        result = true;
       
    return result;   
}

void QTAccessFile::GetRealm(StrPtrLen* accessRealm, StrPtrLen* ioRealmNameStr, char *userName,void *extraDataPtr )
{
    
    if (ioRealmNameStr->Len <= accessRealm->Len) 
        accessRealm->Len = ioRealmNameStr->Len -1; // just copy what we can
    ::memcpy(ioRealmNameStr->Ptr, accessRealm->Ptr,accessRealm->Len);
    ioRealmNameStr->Ptr[accessRealm->Len] = 0; 

}

Bool16 QTAccessFile::TestUser(StrPtrLen* accessUser, char *userName,void *extraDataPtr )
{
   Bool16 result = false;
    
    if ( accessUser->Equal(userName) ) 
        result = true;

  return result;   
}

Bool16 QTAccessFile::TestGroup( StrPtrLen* accessGroup, char *userName, char**groupArray, UInt32 numGroups, void *extraDataPtr)
{
   
    for (UInt32 index = 0; index < numGroups; index ++)
    {   if(accessGroup->Equal(groupArray[index])) 
            return true;
    }

  return false;   

}

Bool16 QTAccessFile::TestExtraData( StrPtrLen* wordPtr, StringParser* lineParserPtr, void* extraDataPtr)
{
    return false;
}


Bool16 QTAccessFile::AccessAllowed  (   char *userName, char**groupArray, UInt32 numGroups, StrPtrLen *accessFileBufPtr,
                                        QTSS_ActionFlags inFlags,StrPtrLen* ioRealmNameStr, Bool16 *outAllowAnyUserPtr, void *extraDataPtr
                                    )
{       
    if (NULL == accessFileBufPtr || NULL == accessFileBufPtr->Ptr || 0 == accessFileBufPtr->Len)
        return true; // nothing to check
        
    if (ioRealmNameStr != NULL && ioRealmNameStr->Ptr != NULL && ioRealmNameStr->Len > 0)
        ioRealmNameStr->Ptr[0] = 0;
        
    StringParser            accessFileParser(accessFileBufPtr);
    QTSS_ActionFlags        currentFlags = qtssActionFlagsRead; 
    StrPtrLen               line;
    StrPtrLen               word;
    Bool16                  haveUserName = false;
    Bool16                  haveRealmResultBuffer = false;
    Bool16                  haveGroups = false;
    
    *outAllowAnyUserPtr = false;
        
    haveUserName = HaveUser(userName, extraDataPtr);
  
    haveGroups = HaveGroups(groupArray, numGroups, extraDataPtr);

    haveRealmResultBuffer =  HaveRealm(userName, ioRealmNameStr, extraDataPtr );
  
    while( accessFileParser.GetDataRemaining() != 0 ) 
    {
        accessFileParser.GetThruEOL(&line);  // Read each line  
        StringParser lineParser(&line);
        lineParser.ConsumeWhitespace();//skip over leading whitespace
        if (lineParser.GetDataRemaining() == 0) // must be an empty line
            continue;

        char firstChar = lineParser.PeekFast();
        if ( (firstChar == '#') || (firstChar == '\0') )
            continue; //skip over comments and blank lines...
        
        lineParser.ConsumeUntilWhitespace(&word);
        if ( word.Equal("<Limit") ) // a limit line
        {
            currentFlags = qtssActionFlagsNoFlags; // clear to no access
            lineParser.ConsumeWhitespace();
            lineParser.ConsumeUntil( &word, sWhitespaceAndGreaterThanMask); // the flag <limit Read> or <limit Read >
            while (word.Len != 0) // compare each word in the line
            {   
                if (word.Equal("WRITE")  ) 
                {   currentFlags |= inFlags & qtssActionFlagsWrite; // accept following lines if inFlags has write
                }
                
                if (word.Equal("READ") ) 
                {   currentFlags |= inFlags & qtssActionFlagsRead; // accept following lines if inFlags has read
                }
                lineParser.ConsumeWhitespace();
                lineParser.ConsumeUntil(&word, sWhitespaceAndGreaterThanMask);
            }
            continue; //done with limit line
        }
        if ( word.Equal("</Limit>") )
            currentFlags = qtssActionFlagsRead; // set the current access state to the default of read access
        
        if (0 == (currentFlags & inFlags))
            continue; // ignore lines because inFlags doesn't match the current access state
            
        if (haveRealmResultBuffer && word.Equal("AuthName") ) //realm name
        {   
            lineParser.ConsumeWhitespace();
            lineParser.GetThruEOL(&word);
            StringParser::UnQuote(&word);// if the parsed string is surrounded by quotes then remove them.
            
            GetRealm(&word, ioRealmNameStr, userName, extraDataPtr );

            // we don't change the buffer len ioRealmNameStr->Len because we might have another AuthName tag to copy
            continue; // done with AuthName (realm)
        }
        
        
        if (word.Equal("require") )
        {
            lineParser.ConsumeWhitespace();
            lineParser.ConsumeUntilWhitespace(&word);       

            if (haveUserName && word.Equal("valid-user") ) 
            {   return true; 
            }
            
            if ( word.Equal("any-user")  ) 
            {   
                *outAllowAnyUserPtr = true;
                return true; 
            }
    
            if (!haveUserName)
                continue;
                
            if (word.Equal("user") )
            {   
                lineParser.ConsumeWhitespace();
                lineParser.ConsumeUntilWhitespace(&word);   
                    
                while (word.Len != 0) // compare each word in the line
                {   
                    if (TestUser(&word, userName, extraDataPtr ))
                        return true;

                    lineParser.ConsumeWhitespace();
                    lineParser.ConsumeUntilWhitespace(&word);       
                }
                continue; // done with "require user" line
            }
            
            if (haveGroups && word.Equal("group")) // check if we have groups for the user
            {
                lineParser.ConsumeWhitespace();
                lineParser.ConsumeUntilWhitespace(&word);       
                
                while (word.Len != 0) // compare each word in the line
                {   
                    if (TestGroup(&word, userName, groupArray, numGroups, extraDataPtr) )
                        return true;

                    lineParser.ConsumeWhitespace();
                    lineParser.ConsumeUntilWhitespace(&word);
                }
                continue; // done with "require group" line
            }
            
            if (TestExtraData(&word, &lineParser, extraDataPtr))
                return true;
                
                    
            continue; // done with unparsed "require" line
        }
        
    }
    
    return false; // user or group not found
}

char*  QTAccessFile::GetAccessFile_Copy( const char* movieRootDir, const char* dirPath)
{   
    OSMutexLocker locker(sAccessFileMutex);

    char* currentDir= NULL;
    char* lastSlash = NULL;
    int movieRootDirLen = ::strlen(movieRootDir);
    int maxLen = strlen(dirPath)+strlen(sQTAccessFileName) + strlen(kPathDelimiterString) + 1;
    currentDir = NEW char[maxLen];

    ::strcpy(currentDir, dirPath);

    //strip off filename
    lastSlash = ::strrchr(currentDir, kPathDelimiterChar);
    if (lastSlash != NULL)
        lastSlash[0] = '\0';
    
    //check qtaccess files
    
    while ( true )  //walk backward up the dir tree.
    {
        int curLen = strlen(currentDir) + strlen(sQTAccessFileName) + strlen(kPathDelimiterString);

        if ( curLen >= maxLen )
            break;
    
        ::strcat(currentDir, kPathDelimiterString);
        ::strcat(currentDir, sQTAccessFileName);
    
        QTSS_Object fileObject = NULL;
        if( QTSS_OpenFileObject(currentDir, qtssOpenFileNoFlags, &fileObject) == QTSS_NoErr) 
        {
            (void)QTSS_CloseFileObject(fileObject);
            return currentDir;
        }
                
        //strip off the "/qtaccess"
        lastSlash = ::strrchr(currentDir, kPathDelimiterChar);
        lastSlash[0] = '\0';
            
        //strip of the tailing directory
        lastSlash = ::strrchr(currentDir, kPathDelimiterChar);
        if (lastSlash == NULL)
            break;
        else
            lastSlash[0] = '\0';
        
        if ( (lastSlash-currentDir) < movieRootDirLen ) //bail if we start eating our way out of fMovieRootDir
            break;
    }
    
    delete[] currentDir;
    return NULL;
}

// allocates memory for outUsersFilePath and outGroupsFilePath - remember to delete
// returns the auth scheme
QTSS_AuthScheme QTAccessFile::FindUsersAndGroupsFilesAndAuthScheme(char* inAccessFilePath, QTSS_ActionFlags inAction, char** outUsersFilePath, char** outGroupsFilePath)
{
    QTSS_AuthScheme authScheme = qtssAuthNone;
    QTSS_ActionFlags currentFlags = qtssActionFlagsRead;
    
    if (inAccessFilePath == NULL)
    return authScheme;
        
    *outUsersFilePath = NULL;
    *outGroupsFilePath = NULL;
    //Assert(outUsersFilePath == NULL);
    //Assert(outGroupsFilePath == NULL);
    
    StrPtrLen accessFileBuf;
    (void)QTSSModuleUtils::ReadEntireFile(inAccessFilePath, &accessFileBuf);
    OSCharArrayDeleter accessFileBufDeleter(accessFileBuf.Ptr);
    
    StringParser accessFileParser(&accessFileBuf);
    StrPtrLen line;
    StrPtrLen word;
    
    while( accessFileParser.GetDataRemaining() != 0 ) 
    {
        accessFileParser.GetThruEOL(&line);     // Read each line   
        StringParser lineParser(&line);
        lineParser.ConsumeWhitespace();         //skip over leading whitespace
        if (lineParser.GetDataRemaining() == 0)     // must be an empty line
            continue;

        char firstChar = lineParser.PeekFast();
        if ( (firstChar == '#') || (firstChar == '\0') )
            continue;               //skip over comments and blank lines...
        
        lineParser.ConsumeUntilWhitespace(&word);
               
        if ( word.Equal("<Limit") ) // a limit line
        {
            currentFlags = qtssActionFlagsNoFlags; // clear to no access
            lineParser.ConsumeWhitespace();
            lineParser.ConsumeUntil( &word, sWhitespaceAndGreaterThanMask); // the flag <limit Read> or <limit Read >
            while (word.Len != 0) // compare each word in the line
            {   
                if (word.Equal("WRITE")  ) 
                {   
                    currentFlags |= inAction & qtssActionFlagsWrite; // accept following lines if inFlags has write
                }
                    
                if (word.Equal("READ") ) 
                {   
                    currentFlags |= inAction & qtssActionFlagsRead; // accept following lines if inFlags has read
                }
                            
                lineParser.ConsumeWhitespace();
                lineParser.ConsumeUntil(&word, sWhitespaceAndGreaterThanMask);
            }
            continue; //done with limit line
        }
        
        if ( word.Equal("</Limit>") )
            currentFlags = qtssActionFlagsRead; // set the current access state to the default of read access
        
        if (0 == (currentFlags & inAction))
            continue; // ignore lines because inFlags doesn't match the current access state
            
        if (word.Equal("AuthUserFile") )
        {
            lineParser.ConsumeWhitespace();
            lineParser.GetThruEOL(&word);
            StringParser::UnQuote(&word);// if the parsed string is surrounded by quotes then remove them.
    
            if(*outUsersFilePath != NULL)       // we are encountering the AuthUserFile keyword twice!
                delete[] *outUsersFilePath; // The last one found takes precedence...delete the previous path
            
            *outUsersFilePath = word.GetAsCString();
            continue;
        }
        
        if (word.Equal("AuthGroupFile") )
        {
            lineParser.ConsumeWhitespace();
            lineParser.GetThruEOL(&word);
            StringParser::UnQuote(&word);// if the parsed string is surrounded by quotes then remove them.

            if(*outGroupsFilePath != NULL)      // we are encountering the AuthGroupFile keyword twice!
                delete[] *outGroupsFilePath;    // The last one found takes precedence...delete the previous path       
            
            *outGroupsFilePath = word.GetAsCString();
            
            continue;
        }
        
        if (word.Equal("AuthScheme") )
        {
            lineParser.ConsumeWhitespace();
            lineParser.GetThruEOL(&word);
            StringParser::UnQuote(&word);// if the parsed string is surrounded by quotes then remove them.

            if (word.Equal("basic"))
                authScheme = qtssAuthBasic;
            else if (word.Equal("digest"))
                authScheme = qtssAuthDigest;
                
            continue;
        }
    }
    
    return authScheme;
}

QTSS_Error QTAccessFile::AuthorizeRequest(QTSS_StandardRTSP_Params* inParams, Bool16 allowNoAccessFiles, QTSS_ActionFlags noAction, QTSS_ActionFlags authorizeAction, Bool16 *outAuthorizedPtr, Bool16 *outAllowAnyUserPtr)
{
    if  ( (NULL == inParams) || (NULL == inParams->inRTSPRequest) || (NULL == outAllowAnyUserPtr) || (NULL == outAuthorizedPtr)  )
        return QTSS_RequestFailed;

    *outAllowAnyUserPtr = false;
    *outAuthorizedPtr = false;
    
    QTSS_RTSPRequestObject  theRTSPRequest = inParams->inRTSPRequest;
    
    // get the type of request
    // Don't touch write requests
    QTSS_ActionFlags action = QTSSModuleUtils::GetRequestActions(theRTSPRequest);
    if(action == qtssActionFlagsNoFlags)
        return QTSS_RequestFailed;
    
    if( (action & noAction) != 0)
        return QTSS_NoErr; // we don't handle
    
    //get the local file path
    char*   pathBuffStr = QTSSModuleUtils::GetLocalPath_Copy(theRTSPRequest);
    OSCharArrayDeleter pathBuffDeleter(pathBuffStr);
    if (NULL == pathBuffStr)
        return QTSS_RequestFailed;

    //get the root movie directory
    char*   movieRootDirStr = QTSSModuleUtils::GetMoviesRootDir_Copy(theRTSPRequest);
    OSCharArrayDeleter movieRootDeleter(movieRootDirStr);
    if (NULL == movieRootDirStr)
        return QTSS_RequestFailed;
    
    QTSS_UserProfileObject theUserProfile = QTSSModuleUtils::GetUserProfileObject(theRTSPRequest);
    if (NULL == theUserProfile)
        return QTSS_RequestFailed;

    char* accessFilePath = QTAccessFile::GetAccessFile_Copy(movieRootDirStr, pathBuffStr);
    OSCharArrayDeleter accessFilePathDeleter(accessFilePath);
        
    char* username = QTSSModuleUtils::GetUserName_Copy(theUserProfile);
    OSCharArrayDeleter usernameDeleter(username);

    UInt32 numGroups = 0;
    char** groupCharPtrArray =  QTSSModuleUtils::GetGroupsArray_Copy(theUserProfile, &numGroups);
    OSCharPointerArrayDeleter groupCharPtrArrayDeleter(groupCharPtrArray);
    
    StrPtrLen accessFileBuf;
    (void)QTSSModuleUtils::ReadEntireFile(accessFilePath, &accessFileBuf);
    OSCharArrayDeleter accessFileBufDeleter(accessFileBuf.Ptr);
        
    if (accessFileBuf.Len == 0 && !allowNoAccessFiles)
    {   accessFileBuf.Set(sAccessValidUser);
        if (DEBUG_QTACCESS) 
            qtss_printf("QTAccessFile::AuthorizeRequest SET Accessfile valid user for no accessfile %s\n", sAccessValidUser);
    }
      
    if (accessFileBuf.Len == 0 && allowNoAccessFiles)
    {   accessFileBuf.Set(sAccessAnyUser);
        if (DEBUG_QTACCESS) 
            qtss_printf("QTAccessFile::AuthorizeRequest SET Accessfile any user for no access file %s\n", sAccessAnyUser);
    }
      
    char realmName[kBuffLen] = { 0 };
    StrPtrLen   realmNameStr(realmName,kBuffLen -1);
    
    //check if this user is allowed to see this movie
    Bool16 allowRequest = this->AccessAllowed(username, groupCharPtrArray, numGroups,  &accessFileBuf, authorizeAction,&realmNameStr, outAllowAnyUserPtr );
    debug_printf("accessFile.AccessAllowed for user=%s returned %d\n", username, allowRequest);
    
    // Get the auth scheme
    QTSS_AuthScheme theAuthScheme = qtssAuthNone;
    UInt32 len = sizeof(theAuthScheme);
    QTSS_Error theErr = QTSS_GetValue(theRTSPRequest, qtssRTSPReqAuthScheme, 0, (void*)&theAuthScheme, &len);
    Assert(len == sizeof(theAuthScheme));
    if(theErr != QTSS_NoErr)
        return theErr;
    
    // If auth scheme is basic and the realm is present in the access file, use it
    if((theAuthScheme == qtssAuthBasic) && (realmNameStr.Ptr[0] != '\0'))   //set the realm if we have one
        (void) QTSS_SetValue(theRTSPRequest,qtssRTSPReqURLRealm, 0, realmNameStr.Ptr, ::strlen(realmNameStr.Ptr));
    else // if auth scheme is basic and no realm is present, or if the auth scheme is digest, use the realm from the users file
    {  
        char*   userRealm = NULL;   
        (void) QTSS_GetValueAsString(theUserProfile, qtssUserRealm, 0, &userRealm);
        if(userRealm != NULL)
        {
            OSCharArrayDeleter userRealmDeleter(userRealm);
            (void) QTSS_SetValue(theRTSPRequest,qtssRTSPReqURLRealm, 0, userRealm, ::strlen(userRealm));
        }
    }
    
    *outAuthorizedPtr = allowRequest;
    
    Bool16 founduser = this->HaveUser(username, NULL);
    Bool16 authContinue = true;
    char nameBuff[256];
    StrPtrLen reqNameStr(nameBuff, kBuffLen);
    StrPtrLen profileNameStr(username);
    theErr = QTSS_GetValue (theRTSPRequest,qtssRTSPReqUserName,0, (void *) reqNameStr.Ptr, &reqNameStr.Len);
 
    
if (DEBUG_QTACCESS)
{   qtss_printf("QTAccessFile::AuthorizeRequest qtaccess profile user =%s ", username);
    reqNameStr.PrintStr("request user=","\n");
    qtss_printf("QTAccessFile::AuthorizeRequest allowRequest=%d founduser=%d authContinue=%d\n", allowRequest, founduser, authContinue);
}   
    if (allowRequest && founduser)
        theErr = QTSSModuleUtils::AuthorizeRequest(theRTSPRequest, &allowRequest, &founduser,&authContinue);
    if (!allowRequest && !founduser)
        theErr = QTSSModuleUtils::AuthorizeRequest(theRTSPRequest, &allowRequest, &founduser,&authContinue);

if (DEBUG_QTACCESS)
{   qtss_printf("QTAccessFile::AuthorizeRequest QTSSModuleUtils::AuthorizeRequest qtaccess profile user =%s ", username);
    reqNameStr.PrintStr("request user=","\n");
    qtss_printf("QTAccessFile::AuthorizeRequest allowRequest=%d founduser=%d authContinue=%d\n", allowRequest, founduser, authContinue);
}   
 
    return theErr;
}


bool DSAccessFile::CheckGroupMembership(const char* inUsername, const char* inGroupName)
{   
    struct passwd   *user	= NULL;
    struct group    *group	= NULL;
#if __MacOSX__
    uuid_t          userID;
    uuid_t          groupID;
    int             isMember	= 0;
#else
    int numGroups = 50;
    gid_t  *groupList;
    groupList = (gid_t *) malloc(numGroups * sizeof(gid_t));
#endif    

    // Look up the user using the POSIX APIs: only care about the UID.
    user = getpwnam(inUsername);
    if ( user == NULL )
            return false;
#if __MacOSX__
    uuid_clear(userID);
    if ( mbr_uid_to_uuid(user->pw_uid, userID) )
            return false;
#endif
    // Look up the group using the POSIX APIs: only care about the GID.
    group = getgrnam(inGroupName);
    endgrent();
    if ( group == NULL )
            return false;
#if __MacOSX__
    uuid_clear(groupID);
    if ( mbr_gid_to_uuid(group->gr_gid, groupID) )
            return false;

    // In Tiger, group membership is painfully simple: we ask memberd for it!
    // mbr_check_membership() returns 0 on success and error code on failure.
    if ( mbr_check_membership(userID, groupID, &isMember) )
            return false;
    return (bool)isMember;
#else 
    if(getgrouplist(inUsername, user->pw_gid, groupList, &numGroups) == -1) {
        qtss_printf("QTAccessFile::CheckGroupMembership getgrouplist for user %s returned -1 and numGroups = %d", inUsername, numGroups);
        return false;
    }
    for(int i = 0; i < numGroups; i++) {
        if(groupList[i] == group->gr_gid) {
            return true;
        }
    }
    return false;
#endif
}

Bool16 DSAccessFile::ValidUser( char*userName, void* extraDataPtr)
{
    struct passwd	*user = getpwnam(userName);
    Bool16 result =true;
    if ( user == NULL )
    {    
         return result;
    }
        
    return true;
}



