/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2001 Apple Computer, Inc.  All Rights Reserved. The
 * contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License.  Please
 * obtain a copy of the License at http://www.apple.com/publicsource and
 * read it before using this file.
 *
 * This 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:		QTSSDemoModule.cpp

	Contains:	Implementation of QTSSDemoModule, a modified version of the QTSSAccessModule
				for demoing at QuickTime Live! and distributing as sample code. 
					


*/

#include "QTSSDemoModule.h"
#include "AccessCheck.h"
#include "StrPtrLen.h"
#include "QTSSModuleUtils.h"
#include "OSArrayObjectDeleter.h"


// ATTRIBUTES

// STATIC DATA


static StrPtrLen sRedirect("RTSP/1.0 302 Found\r\nServer: QTSS/2.0\r\nCSeq: 2\r\nLocation: rtsp://");
static StrPtrLen sRedirectEnd("/error/errormovie.mov\r\n\r\n");

const UInt32 kBuffLen = 512;


// FUNCTION PROTOTYPES

static QTSS_Error QTSSDemoModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register();
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error Shutdown();
static QTSS_Error RereadPrefs();
static QTSS_Error Authenticate(QTSS_StandardRTSP_Params* inParams);
static bool QTSSAccess(QTSS_StandardRTSP_Params* inParams, const char* pathBuff, const char* movieRootDir, char* ioRealmName);


// FUNCTION IMPLEMENTATIONS

QTSS_Error QTSSDemoAuthorizationModule_Main(void* inPrivateArgs)
{
	return _stublibrary_main(inPrivateArgs, QTSSDemoModuleDispatch);
}


QTSS_Error 	QTSSDemoModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
	switch (inRole)
	{
		case QTSS_Register_Role:
			return Register();
		case QTSS_Initialize_Role:
			return Initialize(&inParams->initParams);
		case QTSS_RereadPrefs_Role:
			return RereadPrefs();
		case QTSS_RTSPAuthorize_Role:
			return Authenticate(&inParams->rtspRequestParams);
		case QTSS_Shutdown_Role:
			return Shutdown();
	}
	return QTSS_NoErr;
}


QTSS_Error Register()
{
	// Do role & attribute setup
	(void)QTSS_AddRole(QTSS_Initialize_Role);
	(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
	(void)QTSS_AddRole(QTSS_RTSPAuthorize_Role);
		
	return QTSS_NoErr;
}


QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
//	printf("QTSSDemoModule was just called in the QTSS_Initialize_Role\n");
	// Setup module utils
	QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);

	RereadPrefs();

	return QTSS_NoErr;
}

QTSS_Error Shutdown()
{
	return QTSS_NoErr;
}

QTSS_Error RereadPrefs()
{
	return QTSS_NoErr;
}

bool QTSSAccess(QTSS_StandardRTSP_Params* inParams, 
				const char* pathBuff, 
				const char* movieRootDir,
				char* ioRealmName)
{
	QTSS_Error theErr = QTSS_NoErr;
	printf("In QTSSAccess. Pathbuff: %s. movierootdir: %s\n", pathBuff, movieRootDir);
	
	char passwordBuff[kBuffLen];
	StrPtrLen passwordStr(passwordBuff, kBuffLen);
	
	char nameBuff[kBuffLen];
	StrPtrLen nameStr(nameBuff, kBuffLen);
	
	char realmNameBuff[kBuffLen];
	StrPtrLen realmName(realmNameBuff, kBuffLen);

        //AccessChecker accessChecker(movieRootDir, "/etc/streaming/QTSSUsers", "qtaccess", "/etc/streaming/QTSSGroups");
        AccessChecker accessChecker(movieRootDir, "qtaccess", "/etc/streaming/QTSSUsers", "/etc/streaming/QTSSGroups");
	
	//If there are no access files, then allow world access
	if ( !accessChecker.GetAccessFile(pathBuff) ) 
		return true;

	printf("Found access file\n");
	::strcpy(ioRealmName, accessChecker.GetRealmHeaderPtr());

	//
	// If this RTSP request includes a user name and password, the server decodes
	// that information and stores it in these attributes. 
	theErr = QTSS_GetValue (inParams->inRTSPRequest,qtssRTSPReqUserName,0, (void *) nameStr.Ptr, &nameStr.Len);
	if ( (QTSS_NoErr != theErr) || (nameStr.Len >= kBuffLen) ) return false;	
				
	theErr = QTSS_GetValue (inParams->inRTSPRequest,qtssRTSPReqUserPassword,0, (void *) passwordStr.Ptr, &passwordStr.Len);
	if ( (QTSS_NoErr != theErr) || (passwordStr.Len >= kBuffLen) ) return false;		

	nameBuff[nameStr.Len] = '\0';
	passwordBuff[passwordStr.Len] = '\0';
	
	//
	// Use the name and password to check access
	if ( !accessChecker.CheckAccess(nameBuff, passwordBuff) )
	{
#if 0
		if (nameStr.Len > 0)
		{
			//
			// If the user HAS provided a username, and that username is incorrect, redirect them to
			// an error movie.
			
			//
			// Get the local IP addr as a string so we can properly construct a complete
			// rtsp:// URL.
			char* theLocalAddrStr = NULL;
			UInt32 theAddrLen = 0;

			theErr = QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesLocalAddrStr, 0,
										(void**)&theLocalAddrStr, &theAddrLen);			
			if ( QTSS_NoErr != theErr )
				return false;

			//
			// In order to send the redirect, we need to get a QTSS_StreamRef
			// so we can send data to the client. Get the QTSS_StreamRef out of the request.
			QTSS_StreamRef* theStreamRef = NULL;
			UInt32 strRefLen = 0;
		
			theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqStreamRef, 0,
														(void**)&theStreamRef, &strRefLen);
			if (( QTSS_NoErr != theErr ) || ( sizeof(QTSS_StreamRef) != strRefLen) )
				return false;
		
			//
			// Now that we have the QTSS_StreamRef, send the 302 Moved Temporarily response to the client.
			// Because this is a simple demo, we have a hard-coded location for the error movie.
			UInt32 theLenWritten = 0;
			(void)QTSS_Write(*theStreamRef, sRedirect.Ptr, sRedirect.Len, &theLenWritten, 0);
			(void)QTSS_Write(*theStreamRef, theLocalAddrStr, theAddrLen, &theLenWritten, 0);
			(void)QTSS_Write(*theStreamRef, sRedirectEnd.Ptr, sRedirectEnd.Len, &theLenWritten, 0);
		}
#endif
		printf("Access fail\n");
		return false;
	}
	printf("Access success\n");
	return true;
}

#if 0
QTSS_Error Authenticate(QTSS_StandardRTSP_Params* inParams)
{
	QTSS_Error 				theErr = QTSS_NoErr;
	UInt32 					buffLen = 0;
	QTSS_RTSPRequestObject	theRTSPRequest = inParams->inRTSPRequest;
	
	buffLen = kBuffLen - 1;
	char pathBuff[kBuffLen] = {};
	
	theErr = QTSS_GetValue (theRTSPRequest,qtssRTSPReqLocalPath,0,
								(void *) &pathBuff[0], &buffLen);
	if ( (theErr != QTSS_NoErr) || (0 == buffLen) )
		return theErr; // Bail on the request. The Server will handle the error
	
	pathBuff[buffLen] = '\0';
#if 0
	printf("QTSSDemoModule was just called in the QTSS_Authorize_Role.\n");
	printf("The full path to the file specified by this request is: %s\n",	pathBuff);
#endif

	buffLen = kBuffLen -1;
	char* movieRootDir = NULL;
	theErr = QTSS_GetValuePtr(theRTSPRequest, qtssRTSPReqRootDir, 0,
								(void**)&movieRootDir, &buffLen );
	if ( (theErr != QTSS_NoErr) || (0 == buffLen) )
		return theErr; // Bail on the request. The Server will handle the error

	char realmName[kBuffLen];
	Bool16 allowRequest = ::QTSSAccess(inParams, pathBuff, movieRootDir, realmName);
	if (allowRequest) 
		return theErr; // Bail on the request. The Server will handle the error
	//	
	// We are denying the request so pass false back to the server.
	//
	theErr = QTSS_SetValue(theRTSPRequest,qtssRTSPReqUserAllowed, 0, &allowRequest, sizeof(allowRequest));
	if (theErr != QTSS_NoErr) 
		return theErr; // Bail on the request. The Server will handle the error
	
	theErr = QTSS_SetValue(theRTSPRequest,qtssRTSPReqURLRealm, 0, realmName, strlen(realmName) );
	if (theErr != QTSS_NoErr) 
		return theErr; // Bail on the request. The Server will handle the error

	return theErr;
}
#endif

QTSS_Error Authenticate(QTSS_StandardRTSP_Params* inParams)
{
	QTSS_RTSPRequestObject	theRTSPRequest = inParams->inRTSPRequest;
	
	if  ( (NULL == inParams) || (NULL == inParams->inRTSPRequest) )
		return QTSS_RequestFailed;
	
	//get the local file path
	char*	pathBuffStr = NULL;
	QTSS_Error theErr = QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqLocalPath, 0, &pathBuffStr);
	OSCharArrayDeleter pathBuffDeleter(pathBuffStr);
	if (theErr != QTSS_NoErr)
		return QTSS_RequestFailed;	

	//get the root movie directory
	char*	movieRootDirStr = NULL;
	theErr = QTSS_GetValueAsString(theRTSPRequest,qtssRTSPReqRootDir, 0, &movieRootDirStr);
	OSCharArrayDeleter movieRootDeleter(movieRootDirStr);
	if (theErr != QTSS_NoErr)
		return false;

	//check if this user is allowed to see this movie
	char realmName[kBuffLen] = { 0 };
	StrPtrLen	realmNameStr(realmName,kBuffLen -1);
	Bool16 allowRequest = ::QTSSAccess(inParams, pathBuffStr, movieRootDirStr, realmName);


	if ( realmName[0] != '\0' ) 	//set the realm if we have one
	{
		(void) QTSS_SetValue(theRTSPRequest,qtssRTSPReqURLRealm, 0, realmName, strlen(realmName) );
	}
	

	if (allowRequest)
	{
		return QTSS_NoErr;	//everything's kosher - let the request continue
	}
	else	//request denied
	{
		// We are denying the request so pass false back to the server.
		theErr = QTSS_SetValue(theRTSPRequest,qtssRTSPReqUserAllowed, 0, &allowRequest, sizeof(allowRequest));
		if (theErr != QTSS_NoErr) 
			return QTSS_RequestFailed; // Bail on the request. The Server will handle the error
		
	}

	return QTSS_NoErr;
}





