/*
 *
 * @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:		QTSSHttpFileModule.cpp

	Contains:	Implements the HTTP file module
 */


#ifndef __Win32__
#include <unistd.h>
#include <dirent.h>
#endif

		
#include <stdio.h>		
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "QTSSHttpFileModule.h"
#include "OSArrayObjectDeleter.h"
#include "StringParser.h"
#include "StrPtrLen.h"
#include "OSMemory.h"
#include "OSHeaders.h"
#include "ev.h"
#include "QTFile.h"
#include "QTTrack.h"
#include "QTHintTrack.h"
#include "QTSSModuleUtils.h"
#include "QTSSRollingLog.h"
#include "OSMutex.h"
#include "HTTPProtocol.h"
#include "HTTPRequest.h"

#define HTTP_FILE_ASYNC 1
#define HTTP_FILE_DEBUGGING 0

class QTSSHttpAccessLog;

// STATIC DATA
// For processing the requests
static QTSS_ModulePrefsObject sPrefs = NULL;
static QTSS_PrefsObject sServerPrefs = NULL;
static QTSS_ServerObject sServer = NULL;


static StrPtrLen	sPathSeparator("/");
static StrPtrLen	sRTSPUrlPrefix("rtsp://");
static StrPtrLen	sRefMovieBufPrefix("rtsptext\r");
static StrPtrLen	sRespHeaderPrefix("HTTP/1.0 200 OK\r\nServer: QTSS/2.0\r\nConnection: Close");
static StrPtrLen	sContentLengthHeaderTag("\r\nContent-Length: ");
static StrPtrLen	sContentTypeHeaderTag("\r\nContent-Type: ");
static StrPtrLen 	sConnectionKeepAliveTag("Keep-Alive");
static StrPtrLen	sQuickTimeMimeType("video/quicktime");
static StrPtrLen	sUnknownMimeType("application/unknown");
static StrPtrLen	sGifMimeType("image/gif");
static StrPtrLen	sSdpMimeType("application/sdp");
static StrPtrLen	sSmilMimeType("application/smil");
static StrPtrLen	sQTSuffix("qt");
static StrPtrLen	sMovSuffix("mov");
static StrPtrLen	sGifSuffix("gif");
static StrPtrLen	sSdpSuffix("sdp");
static StrPtrLen	sSmiSuffix("smi");
static StrPtrLen	sSmilSuffix("smil");

static const UInt32 kReadingBufferState = 0;
static const UInt32 kWritingBufferState = 1;

// For logging the requests
// Default values for preferences
static Bool16	sDefaultHTTPFileXferEnabled = false; // This module is not enabled by default.
static Bool16	sDefaultLogEnabled		= false;
static char*	sDefaultLogName		 	= "StreamingServerHttp";
#if __MacOSX__
static char*	sDefaultLogDir		 	= "/Library/QuickTimeStreaming/Logs/";
#elif __Win32__
static char*	sDefaultLogDir		 	= "c:\Program Files\Darwin Streaming Server\httpLogs";
#else
static char*	sDefaultLogDir		 	= "/var/streaming/logs/";
#endif
static UInt32	sDefaultMaxLogBytes 	= 50000000;
static UInt32	sDefaultRollInterval 	= 7;
static char*	sVoidField 				= "-";
// Current values for preferences
static Bool16	sHTTPFileXferEnabled	= false;
static Bool16	sLogEnabled				= false;
static UInt32	sMaxLogBytes 			= 50000000;
static UInt32	sRollInterval 			= 7;

static OSMutex*	sLogMutex = NULL;// Log module isn't reentrant
static QTSSHttpAccessLog* sAccessLog = NULL;

// ATTRIBUTES
// For processing the http requests
static QTSS_AttributeID	sTransferTypeAttr	= qtssIllegalAttrID;
static QTSS_AttributeID	sEventContextAttr	= qtssIllegalAttrID;
static QTSS_AttributeID	sFileAttr			= qtssIllegalAttrID;
static QTSS_AttributeID	sStateAttr			= qtssIllegalAttrID;
static QTSS_AttributeID	sFileBufferAttr		= qtssIllegalAttrID;
static QTSS_AttributeID	sFileBufferLenAttr	= qtssIllegalAttrID;
static QTSS_AttributeID	sReadOffsetAttr		= qtssIllegalAttrID;
static QTSS_AttributeID	sWriteOffsetAttr	= qtssIllegalAttrID;

// For logging the requests
static QTSS_AttributeID sRequestAttr		= qtssIllegalAttrID;
static QTSS_AttributeID sContentLengthAttr	= qtssIllegalAttrID;

class QTSSHttpAccessLog : public QTSSRollingLog
{
	public:
	
		QTSSHttpAccessLog() : QTSSRollingLog() {}
		virtual ~QTSSHttpAccessLog() {}
	
		virtual char* GetLogName() { return QTSSModuleUtils::GetStringAttribute(sPrefs, "http_logfile_name", sDefaultLogName);}
		virtual char* GetLogDir()  { return QTSSModuleUtils::GetStringAttribute(sPrefs, "http_logfile_dir", sDefaultLogDir); }
		virtual UInt32 GetRollIntervalInDays() 	{ return sRollInterval; }
		virtual UInt32 GetMaxLogBytes()			{ return sMaxLogBytes; }
};

// FUNCTIONS
static QTSS_Error 	QTSSHttpFileModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error 	Register(QTSS_Register_Params* inParams);
static QTSS_Error 	Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error 	FilterRequest(QTSS_Filter_Params* inParams);
static QTSS_Error 	CloseRTSPSession(QTSS_RTSPSession_Params* inParams);
// For processing the requests
static UInt32 		GetBitRate(char* filePath);
static void* 		MakeMoov(void* rmda, UInt32 rmdaLen, UInt32* moovLen);
static void* 		MakeRmda(char* url, UInt32 rate, UInt32* rmdaLen);
static StrPtrLen* 	GetMimeType(StrPtrLen* fileName);
// For logging the requests
static QTSS_Error 	RereadPrefs();
static void 		LogRequest(QTSS_RTSPSessionObject inRTSPSession);
static void 		CheckHttpAccessLogState(Bool16 forceEnabled);

// FUNCTION IMPLEMENTATIONS

QTSS_Error QTSSHttpFileModule_Main(void* inPrivateArgs)
{
	return _stublibrary_main(inPrivateArgs, QTSSHttpFileModuleDispatch);
}


QTSS_Error QTSSHttpFileModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
	switch (inRole)
	{
		case QTSS_Register_Role:
			return Register(&inParams->regParams);
		case QTSS_Initialize_Role:
		    return Initialize(&inParams->initParams);
		case QTSS_RTSPFilter_Role:
			return FilterRequest(&inParams->rtspFilterParams);
		case QTSS_RTSPSessionClosing_Role:
			return CloseRTSPSession(&inParams->rtspSessionClosingParams);
	}
	return QTSS_NoErr;
}


QTSS_Error Register(QTSS_Register_Params* inParams)
{
	sLogMutex = new OSMutex();
	
	// Add attributes for processing the requests
	static char*	sTransferTypeName	= "QTSSHttpFileModuleTransferType";
	static char*	sEventContextName 	= "QTSSHttpFileModuleContext";
	static char*	sFileName			= "QTSSHttpFileModuleFile";
	static char*	sStateName 	        = "QTSSHttpFileModuleState";
	static char*	sFileBufferName		= "QTSSHttpFileModuleFileBuffer";
	static char*	sFileBufferLenName	= "QTSSHttpFileModuleFileBufferLen";
	static char*	sReadOffsetName		= "QTSSHttpFileModuleReadOffset";
	static char*	sWriteOffsetName	= "QTSSHttpFileModuleWriteOffset";
	
	// Add attributes for logging the requests
	static char*	sRequestName		= "QTSSHttpFileModuleRequestName";
	static char*	sContentLengthName	= "QTSSHttpFileModuleContentLengthName";

	
	// Do role & attribute setup
	(void)QTSS_AddRole(QTSS_Initialize_Role);
	(void)QTSS_AddRole(QTSS_RTSPFilter_Role);
	(void)QTSS_AddRole(QTSS_RTSPSessionClosing_Role);
	
	// Attributes for processing requests
	(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sTransferTypeName, NULL, qtssAttrDataTypeUInt32);
	(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sTransferTypeName, &sTransferTypeAttr);
	
	(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sEventContextName, NULL, qtssAttrDataTypeUnknown);
	(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sEventContextName, &sEventContextAttr);
	
	(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sFileName, NULL, qtssAttrDataTypeCharArray);
	(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sFileName, &sFileAttr);

	(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sStateName, NULL, qtssAttrDataTypeUnknown);
	(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sStateName, &sStateAttr);
	
	(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sFileBufferName, NULL, qtssAttrDataTypeUnknown);
	(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sFileBufferName, &sFileBufferAttr);

	(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sFileBufferLenName, NULL, qtssAttrDataTypeUInt32);
	(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sFileBufferLenName, &sFileBufferLenAttr);
	
	(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sReadOffsetName, NULL, qtssAttrDataTypeUInt32);
	(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sReadOffsetName, &sReadOffsetAttr);

	(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sWriteOffsetName, NULL, qtssAttrDataTypeUInt32);
	(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sWriteOffsetName, &sWriteOffsetAttr);

	// Attributes for logging requests
	(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sRequestName, NULL, qtssAttrDataTypeUnknown);
	(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sRequestName, &sRequestAttr);
	
	(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sContentLengthName, NULL, qtssAttrDataTypeUInt32);
	(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sContentLengthName, &sContentLengthAttr);
	
	// Tell the server our name!
	static char* sModuleName = "QTSSHttpFileModule";
	::strcpy(inParams->outModuleName, sModuleName);

	return QTSS_NoErr;
}

QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
	// Setup module utils
	QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);

	// Get prefs object
    sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
    sServerPrefs = inParams->inPrefs;
    sServer = inParams->inServer;
    
    // This creates the log
    CheckHttpAccessLogState(false);
    
	RereadPrefs();
    
    return QTSS_NoErr;
}

QTSS_Error RereadPrefs()
{
	// Code from Access Log module
	QTSSModuleUtils::GetAttribute(sPrefs, "http_xfer_enabled", 	qtssAttrDataTypeBool16,
								&sHTTPFileXferEnabled, &sDefaultHTTPFileXferEnabled, sizeof(sHTTPFileXferEnabled));
	QTSSModuleUtils::GetAttribute(sPrefs, "http_logging", 	qtssAttrDataTypeBool16,
								&sLogEnabled, &sDefaultLogEnabled, sizeof(sLogEnabled));
	QTSSModuleUtils::GetAttribute(sPrefs, "http_logfile_size", 	qtssAttrDataTypeUInt32,
								&sMaxLogBytes, &sDefaultMaxLogBytes, sizeof(sMaxLogBytes));
	QTSSModuleUtils::GetAttribute(sPrefs, "http_logfile_interval", 	qtssAttrDataTypeUInt32,
								&sRollInterval, &sDefaultRollInterval, sizeof(sRollInterval));
	return QTSS_NoErr;
}

QTSS_Error FilterRequest(QTSS_Filter_Params* inParams)
{
	static UInt32 sFileBufSize = 32768; 
	static UInt32 sZero = 0;
	static Bool16 sFalse = false;
	
	UInt32 theLen = 0;
	UInt32 initialState;
	UInt32* theStateP = NULL;
	DIR* theDirectory = NULL;
	QTSS_Object theFile = NULL;
	QTSS_Error theErr = QTSS_NoErr;
	UInt64 theFileLength = 0;
	
	HTTPRequest* httpRequest = NULL;
	
	QTSS_RTSPRequestObject theRequest = inParams->inRTSPRequest;
	QTSS_RTSPSessionObject theSession = inParams->inRTSPSession;
	
	// If this feature is disabled just return
	if (!sHTTPFileXferEnabled)
		return QTSS_NoErr;
	
	StrPtrLen serverHdr;
	(void)QTSS_GetValuePtr(sServer, qtssSvrRTSPServerHeader, 0, (void**)&serverHdr.Ptr, &serverHdr.Len);
	
	(void)QTSS_GetValuePtr(theSession, sStateAttr, 0, (void**)&theStateP, &theLen);
	if ( (theStateP == NULL) || (theLen != sizeof(UInt32)) )
	{
		// Initial state. 	
		TransferType type = 0;	// Enum datatype defined in QTSSHttpFileModule.h
		StrPtrLen theFullRequest;
		HTTPMethod reqMethod;
		StrPtrLen reqStr;
		UInt32 index;
		StrPtrLen* responseHeader;
		
		(void)QTSS_GetValuePtr(theRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest.Ptr, &theFullRequest.Len);
		httpRequest = NEW HTTPRequest(&serverHdr, &theFullRequest);
		
		theErr = httpRequest->Parse();
		
		// If the header couldn't be parsed, return from the role
		if(theErr != QTSS_NoErr)
			return QTSS_NoErr;
		
		reqMethod = httpRequest->GetMethod();	
		// If is not a HEAD or a GET request, just return
		if ((reqMethod != httpGetMethod) && (reqMethod != httpHeadMethod)) 
			return QTSS_NoErr;
		
		char *reqPath = httpRequest->GetRequestPath();
		if(::strcmp(reqPath, "") != 0) 
		{
			reqStr.Ptr = reqPath;
			reqStr.Len = ::strlen(reqPath);
		}
		
		// If a filename or directory is not given in the request, just return
		if(reqStr.Ptr == NULL)
			return QTSS_NoErr;	  
			
		StrPtrLen* requestLine = httpRequest->GetRequestLine();
		// Store the request line in the request attribute for the purposes of logging
		(void)QTSS_SetValue(theSession, sRequestAttr, 0, requestLine, sizeof(*requestLine));
		
		// Get the movie folder name
		char* movieFolderString = NULL;
		QTSS_Error err = QTSS_GetValueAsString (sServerPrefs, qtssPrefsMovieFolder, 0, &movieFolderString);
		if (err != QTSS_NoErr)
			return QTSS_NoErr;
		
		OSCharArrayDeleter movieFolder(movieFolderString);
		StrPtrLen theMovieFolder(movieFolderString);
		
		// Get the http folder name
		OSCharArrayDeleter httpFolderDtr(QTSSModuleUtils::GetStringAttribute(sPrefs, "http_folder", ""));
		StrPtrLen theHttpFolder(httpFolderDtr.GetObject());
			
		char* thePath = NULL;		
		if ( theMovieFolder.Len != 0 ) 
		{
			// Create the entire path with the movie folder as root directory	
			thePath = NEW char[theMovieFolder.Len + sPathSeparator.Len + reqStr.Len + 1];
			::memcpy(thePath, theMovieFolder.Ptr, theMovieFolder.Len);
			index = theMovieFolder.Len;
			::strcpy(thePath + index, sPathSeparator.Ptr);
			index += sPathSeparator.Len;
			::memcpy(thePath + index, reqStr.Ptr, reqStr.Len);
			index += reqStr.Len;
			thePath[index] = '\0';
			
	   		theDirectory = ::opendir(thePath);
		
			if ( theDirectory == NULL )
			{						// If it's not a directory in the movie folder
#if HTTP_FILE_ASYNC
				theErr = QTSS_OpenFileObject(thePath, qtssOpenFileAsync, &theFile);
#else
				theErr = QTSS_OpenFileObject(thePath, qtssOpenFileNoFlags, &theFile);
#endif
				if ((theErr == QTSS_NoErr)) // If it's a file in the movie folder, 
											// then it's on-the-fly ref movie generation
				{							// for a single movie file
#if HTTP_FILE_DEBUGGING
					printf("Request for on-the-fly generated ref movie of a hinted QuickTime file\n");
#endif 
					type = transferRefMovieFile;
					delete [] thePath;
				}
				else 
				{					// If it's not a file in the movie folder, 
									// then it's not on-the-fly ref movie generation
					if ( theFile != NULL)
						(void)QTSS_CloseFileObject(theFile);
					delete [] thePath;
					type = 0;
				}							
			}
			else					// If it's a directory in the movie folder, then it's on-the-fly ref movie generation
			{						// for a directory of movie files
#if HTTP_FILE_DEBUGGING
				printf("Request for on-the-fly generated ref movie for a directory of hinted QuickTime files\n");
#endif 
				type = transferRefMovieFolder;						 	
			}    
		}
		
		// If the movie folder doesn't exist or it exists and the request is not for a file or a directory
		// in the movie folder
		if ( type == 0 && theHttpFolder.Len != 0 )
		{
			// Create the entire path with the http folder as root directory	
			thePath = NEW char[theHttpFolder.Len + sPathSeparator.Len + reqStr.Len + 1];
			::memcpy(thePath, theHttpFolder.Ptr, theHttpFolder.Len);
			index = theHttpFolder.Len;
			::strcpy(thePath + index, sPathSeparator.Ptr);
			index += sPathSeparator.Len;
			::memcpy(thePath + index, reqStr.Ptr, reqStr.Len);
			index += reqStr.Len;
			thePath[index] = '\0';
		
#if HTTP_FILE_ASYNC
			theErr = QTSS_OpenFileObject(thePath, qtssOpenFileAsync, &theFile);
#else
			theErr = QTSS_OpenFileObject(thePath, qtssOpenFileNoFlags, &theFile);
#endif
			if( (theErr == QTSS_NoErr) )  	// If the file is in the http folder
			{								// it is a  request for progressive download
#if HTTP_FILE_DEBUGGING
				printf("Request for the HTTP transfer of a file\n");
#endif 
				type = transferHttpFile;
				delete [] thePath;
			}
			else
			{										// If the file is not found in the http folder, just return
				if ( theFile != NULL )
					(void)QTSS_CloseFileObject(theFile);
				delete [] thePath;
				type = 0;
#if HTTP_FILE_DEBUGGING
				printf("Request file not found by this module\n");
#endif 
				return QTSS_NoErr;	
			}
		}
		
				
		// If type is still 0, it is neither an HTTP file transfer request nor an on-the-fly ref movie request
		// Just return
		if ( type == 0 )
		{
#if HTTP_FILE_DEBUGGING
			printf("Request file not found by this module\n");
#endif
			return QTSS_NoErr;
		}
			
		// If it's a "Head" request send the Head response header back and just return
		if ( reqMethod == httpHeadMethod)
		{
			httpRequest->CreateResponseHeader(http10Version, httpOK);
			httpRequest->AppendResponseHeader(httpContentTypeHeader, &sQuickTimeMimeType);
			responseHeader = httpRequest->GetCompleteResponseHeader();
			QTSS_Write(theRequest, responseHeader->Ptr, responseHeader->Len, NULL, 0);  
			if ( theDirectory != NULL )
			       ::closedir(theDirectory);
			else if( theFile != NULL )
				(void)QTSS_CloseFileObject(theFile);
			return QTSS_NoErr;
		}

		// Create a buffer to store data.
		char* theFileBuffer;
		char* contentLength = NEW char[256];
		UInt32 headerLength = sZero;
		UInt32 fileBufferLen = sZero;
		
		char serverIPAddrBuf[20] = { 0 };
		StrPtrLen serverIPAddr(serverIPAddrBuf, 19);      // Server name
		
		// Get the server nameif it's a ref movie transfer
		if ( type == transferRefMovieFile || type == transferRefMovieFolder )
		{
			// Get the server ip address
			(void)QTSS_GetValue(theSession, qtssRTSPSesLocalAddrStr, 0, serverIPAddr.Ptr, &serverIPAddr.Len);
	
			if ( serverIPAddr.Len == 0 )
			// The local IP address for this session is not found; the ref movie cannot be created, so return
		    	return QTSS_NoErr;
		}
		
		// Before sending any response, set keep alive to off for this connection
		// Regardless of what the client sends, the server always closes the connection after sending the file
		(void)QTSS_SetValue(theRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse));
		
		switch (type) 
		{
			case transferHttpFile:		
			{								// Allocate memory for theFileBuffer
											theFileBuffer = NEW char[sFileBufSize];  
											httpRequest->CreateResponseHeader(http10Version, httpOK);
											httpRequest->AppendConnectionCloseHeader();
											theLen = sizeof(UInt64);
											theErr = QTSS_GetValue(theFile, qtssFlObjLength, 0, (void*)&theFileLength, &theLen);
											Assert(theErr == QTSS_NoErr);
											Assert(theLen == sizeof(UInt64));
											httpRequest->AppendContentLengthHeader(theFileLength);
											httpRequest->AppendResponseHeader(httpContentTypeHeader, GetMimeType(&reqStr));
											responseHeader = httpRequest->GetCompleteResponseHeader();
											headerLength = responseHeader->Len;
											::memcpy(theFileBuffer, responseHeader->Ptr, responseHeader->Len);
											
											// We need content length string for logging purposes
											::sprintf(contentLength, "%"_64BITARG_"d", theFileLength);
											
											//Delete the http request object ...no use for it anymore
											delete httpRequest;
											
		       								// Set the intial file buffer length to be zero as there is nothing in the file buffer (other than the header).
		       								// The read sets this attribute to the length that needs to be written to the stream
		       								fileBufferLen = sZero;
		       								// Set the state to reading so that file contents can be read into the buffer
		       								initialState = kReadingBufferState;
											break;
			}
			case transferRefMovieFolder:
			{	
#if HTTP_FILE_DEBUGGING
											printf("Creating a ref movie for the folder\n");
											printf("filenames:\n");		
#endif			
											UInt32 fileCount = 0;
											struct dirent* entry;
											// Find the number of .mov files in the requested directory of the movie folder
											while( (entry = ::readdir(theDirectory)) != NULL)
											{
												if( ::strcmp(entry->d_name + ::strlen(entry->d_name) - 4, ".mov") == 0 )
													fileCount ++;
												
											}
#if HTTP_FILE_DEBUGGING
											printf("Number of .mov files in the directory: %lu\n", fileCount);
#endif			
											// Go back to the beginning of the directory
											::rewinddir(theDirectory);
											char** fileNames;
											fileNames = NEW char*[fileCount];
											fileCount = 0;
											// Find all the .mov files in the requested directory of the movie folder
											while( (entry = ::readdir(theDirectory)) != NULL)
											{
		        								char* fileName = NEW char[::strlen(entry->d_name) + 1];
		        								::strcpy(fileName, entry->d_name);
		        								if( ::strcmp(fileName + strlen(fileName) - 4, ".mov") == 0 ) 
												{
			        								fileNames[fileCount] = fileName;
			        								fileCount ++;
#if HTTP_FILE_DEBUGGING
													printf("%lu : %s\n", fileCount, fileName);
#endif			
												}
											}
											// Close the directory stream - We no longer have any use for it
											::closedir(theDirectory);
											
											// Create the base url for each of the files ( ex. rtsp://servername/dirname/ )										
											OSCharArrayDeleter baseUrl(NEW char[sRTSPUrlPrefix.Len + serverIPAddr.Len + sPathSeparator.Len + reqStr.Len + sPathSeparator.Len + 1]);
											::memcpy(baseUrl.GetObject(), sRTSPUrlPrefix.Ptr, sRTSPUrlPrefix.Len);
											index = sRTSPUrlPrefix.Len;
											::memcpy(baseUrl.GetObject() + index, serverIPAddr.Ptr, serverIPAddr.Len);
											index += serverIPAddr.Len;
											::strcpy(baseUrl.GetObject() + index, sPathSeparator.Ptr);
											index += sPathSeparator.Len;
											::memcpy(baseUrl.GetObject() + index, reqStr.Ptr, reqStr.Len);
											index += reqStr.Len;
											::strcpy(baseUrl.GetObject() + index, sPathSeparator.Ptr);
											index += sPathSeparator.Len;
											baseUrl.GetObject()[index] = '\0';

											UInt32 *rmdas = NULL, rmdasLen = 0, *moov = NULL, moovLen = 0, *tempRmda = NULL, tempRmdaLen = 0;
											UInt32 *rmdasTemp;
											UInt32 arrayIndex;
											char* filePath;
											char* url;
											UInt32 bitRate;

											for (arrayIndex = 0; arrayIndex < fileCount; arrayIndex++)
											{
												// Get the filePath for each .mov file in the directory
		       									filePath = NEW char[::strlen(thePath) + sPathSeparator.Len + ::strlen(fileNames[arrayIndex]) + 1];
		       									::strcpy(filePath, thePath);
		       									::strcat(filePath, sPathSeparator.Ptr);
		       									::strcat(filePath, fileNames[arrayIndex]);
		       									// Create the complete url from the base url for each .mov file in the directory
		       									url = NEW char[index + ::strlen(fileNames[arrayIndex]) + 1];
		       									::memcpy(url, baseUrl.GetObject(), index);
		       									::strcpy(url + index, fileNames[arrayIndex]);
		       									//Find the approximate bit rate for each .mov file in the directory
		       									bitRate = GetBitRate(filePath);
#if HTTP_FILE_DEBUGGING
		       									printf("%lu\t: Path = %s\n", arrayIndex + 1, filePath);
		       									printf("Url = %s\n", url);
		       									printf("Rate = %lu\n", bitRate); 
#endif
		       									if ( bitRate != 0 )
												{
		       										// Create "rmda" for each .mov file in the directory
		       										tempRmda = (UInt32 *) MakeRmda(url, bitRate, &tempRmdaLen);
												}
		       									
		       									// Deallocate the memory for the fileName, filePath and url
		       									delete [] fileNames[arrayIndex];
		       									delete [] filePath;
		       									delete [] url;
		       									
		      									// If tempRmdaLen is NULL, tempRmdaLen is zero: 
		       									// Just move to next file in the folder 
		      									// if( tempRmda == NULL ) // Couldn't create tempRmda. Plan of action: Barf!
			     								// return QTSS_NoErr;
		       
		       									// Reallocate the rmdas buffer so as to append the tempRmda
		       									if ( tempRmdaLen != 0 )
		       									{
		       										rmdasTemp = rmdas;  // Store the rmdas in a temporary pointer and reallocate	       									
		       										rmdas = NEW UInt32[rmdasLen + tempRmdaLen];
		       										
		       										if( rmdas == NULL ) // Couldn't create rmdas. Barf now!
			     										return QTSS_NoErr;
			     								
			     									::memcpy((char *)rmdas, rmdasTemp, rmdasLen); // Copy old contents and append tempRmda
		       										::memcpy((char *)rmdas + rmdasLen, tempRmda, tempRmdaLen);
		       										rmdasLen += tempRmdaLen;
		       										// Delete the old pointer and the tempRmda pointer
		       										if (rmdasTemp != NULL)
		       											delete rmdasTemp;	
		       										delete tempRmda;
		       									}
											}
											// Delete the array of filenames
											delete [] fileNames;
											// Free the path pointer. We are done with it.
											delete [] thePath;
									
											if ( rmdasLen != 0 )
												// Create a "moov" for the ref movie
												moov = (UInt32 *) MakeMoov(rmdas, rmdasLen, &moovLen);
											
											if( moov == NULL ) // Couldn't create moov after doing everything else!
		      									return QTSS_NoErr;
											
											// Create the HTTP response header
											httpRequest->CreateResponseHeader(http10Version, httpOK);
											httpRequest->AppendConnectionCloseHeader();
											httpRequest->AppendContentLengthHeader(moovLen);
											httpRequest->AppendResponseHeader(httpContentTypeHeader, &sQuickTimeMimeType);
											responseHeader = httpRequest->GetCompleteResponseHeader();
											headerLength = responseHeader->Len;
																						
											// Allocate memory for theFileBuffer
											theFileBuffer = NEW char[headerLength + moovLen];
											
											// Write the HTTP response header into the file buffer
											::memcpy(theFileBuffer, responseHeader->Ptr, responseHeader->Len);	
											
											// We need content length string for logging purposes 
											::sprintf(contentLength, "%lu", moovLen);
											
											//Delete the http request object ...no use for it anymore
											delete httpRequest;
																																		
											// Write the ref movie into the buffer
											::memcpy(theFileBuffer + headerLength, moov, moovLen);
											delete moov;	// Delete the moov pointer
											// Set the size of the file buffer to that of header length + ref movie length
											fileBufferLen = headerLength + moovLen;							
											initialState = kWritingBufferState;
											break;
			}
			case transferRefMovieFile:		
			{
#if HTTP_FILE_DEBUGGING
											printf("Creating a ref movie for the hinted file\n");	
#endif
											// Create a ref movie buffer for the single file. It is of the form:
											//	rtsptext\r
											//	rtsp://servername/filepath
											OSCharArrayDeleter refMovieBuf(NEW char[sRefMovieBufPrefix.Len + sRTSPUrlPrefix.Len + serverIPAddr.Len + sPathSeparator.Len + reqStr.Len + 1]);
											::memcpy(refMovieBuf.GetObject(), sRefMovieBufPrefix.Ptr, sRefMovieBufPrefix.Len);
		       								index = sRefMovieBufPrefix.Len;
		       								::memcpy(refMovieBuf.GetObject() + index, sRTSPUrlPrefix.Ptr, sRTSPUrlPrefix.Len);
		       								index += sRTSPUrlPrefix.Len;
		       								::memcpy(refMovieBuf.GetObject() + index, serverIPAddr.Ptr, serverIPAddr.Len);
		       								index += serverIPAddr.Len;
		       								::strcpy(refMovieBuf.GetObject() + index, sPathSeparator.Ptr);
		       								index += sPathSeparator.Len;
		       								::memcpy(refMovieBuf.GetObject() + index, reqStr.Ptr, reqStr.Len);
		   									index += reqStr.Len;    
		       								refMovieBuf.GetObject()[index] = '\0';
		       								
		       								// Create the HTTP response header
											httpRequest->CreateResponseHeader(http10Version, httpOK);
											httpRequest->AppendConnectionCloseHeader();
											httpRequest->AppendContentLengthHeader(index);
											httpRequest->AppendResponseHeader(httpContentTypeHeader, &sQuickTimeMimeType);
											responseHeader = httpRequest->GetCompleteResponseHeader();
											headerLength = responseHeader->Len;
		       								
		       								// Allocate memory for theFileBuffer
											theFileBuffer = NEW char[headerLength + index];
											// Write the HTTP response header into the file buffer
											::memcpy(theFileBuffer, responseHeader->Ptr, responseHeader->Len);	
																			
		       								// Write the ref movie created above to the file buffer
		       								::memcpy(theFileBuffer + headerLength, refMovieBuf.GetObject(), index);
		       								
		       								// We need content length string for logging purposes 
		       								::sprintf(contentLength, "%lu", index);
		       								
		       								// Write the contents of the file buffer to the request stream and return
		       								QTSS_Write(theRequest, theFileBuffer, (headerLength + index), NULL, 0);
		       								
		       								// Before returning, deallocate theFileBuffer memory
		       								delete [] theFileBuffer;
		       								// Also, delete the file descriptor
		       								
											(void)QTSS_CloseFileObject(theFile);
#if HTTP_FILE_DEBUGGING
											printf("Wrote the ref movie to the request stream. Successful!\n");	
#endif
											// Store the content length string for the purposes of logging
											// Must be done here as we return a response to the client after this.
											(void)QTSS_SetValue(theSession, sContentLengthAttr, 0, &contentLength, sizeof(contentLength));
											// Log the request before returning
											LogRequest(theSession);
											
											//Delete the http request object ...no use for it anymore
											delete httpRequest;
											
		       								return QTSS_NoErr;
											
			}
			default:
			{								// Shouldn't ever happen
#if HTTP_FILE_DEBUGGING
		       								printf("Illegal transfer type: Not a ref movie file/directory or http file transfer\n");
#endif											
											return QTSS_NoErr;
			}
		}
		
												
		// Setup all the dictionary values we need
		// Store the content length string for the purposes of logging
		(void)QTSS_SetValue(theSession, sContentLengthAttr, 0, &contentLength, sizeof(contentLength));
		// Store our initial state
	    (void)QTSS_SetValue(theSession, sStateAttr, 0, &initialState, sizeof(initialState));
	    theStateP = &initialState;
		// Set the file buffer pointer and the file buffer length attributes
		(void)QTSS_SetValue(theSession, sFileBufferAttr, 0, &theFileBuffer, sizeof(theFileBuffer));
		(void)QTSS_SetValue(theSession, sFileBufferLenAttr, 0, &fileBufferLen, sizeof(fileBufferLen));
		// Set the initial write offset to zero so that the buffer can be flushed to the stream from the beginning
		(void)QTSS_SetValue(theSession, sWriteOffsetAttr, 0, &sZero, sizeof(sZero));
		// Set the type of transfer so that only the required attributes can be read for reading/writing states
		(void)QTSS_SetValue(theSession, sTransferTypeAttr, 0, &type, sizeof(type));
		
		// For http file transfer type, store the file pointer, response header length, 
		// and the read offset so that the file can be read into the buffer
		// For Async I/O, Create an event context for the file read and store it in the event context attribute
		if ( type == transferHttpFile )
		{
			// Store the file pointer so that we can read from it
			(void)QTSS_SetValue(theSession, sFileAttr, 0, &theFile, sizeof(theFile));

			// Keep track of the initial write into the buffer, so that we can read from file into the buffer at the correct offset
			(void)QTSS_SetValue(theSession, sReadOffsetAttr, 0, &headerLength, sizeof(headerLength));
		}        

	}
       
	// Get our attributes
	char** theFileBufferP = NULL;					// File buffer pointer
	(void)QTSS_GetValuePtr(theSession, sFileBufferAttr, 0, (void**)&theFileBufferP, &theLen);
	Assert(theFileBufferP != NULL);
	Assert(theLen == sizeof(char**));
	
	UInt32* theFileBufferLenP = NULL;				// File buffer length
	(void)QTSS_GetValuePtr(theSession, sFileBufferLenAttr, 0, (void**)&theFileBufferLenP, &theLen);
	Assert(theFileBufferLenP != NULL);
	Assert(theLen == sizeof(UInt32*));

	UInt32* theWriteOffsetP = NULL;					// Write offset for the file buffer
	(void)QTSS_GetValuePtr(theSession, sWriteOffsetAttr, 0, (void**)&theWriteOffsetP, &theLen);
	Assert(theWriteOffsetP != NULL);
	Assert(theLen == sizeof(UInt32*));
	
	TransferType* theTransferTypeP = NULL;
	(void)QTSS_GetValuePtr(theSession, sTransferTypeAttr, 0, (void**)&theTransferTypeP, &theLen);
	Assert(theTransferTypeP != NULL);
	Assert(theLen == sizeof(TransferType*));

	UInt32 theState = *theStateP;
	UInt32 theFileBufferLen = *theFileBufferLenP;
	UInt32 theWriteOffset = *theWriteOffsetP;
	TransferType theTransferType = *theTransferTypeP;
	
	QTSS_Object* theFileP = NULL;
	UInt32* theReadOffsetP = NULL;
	UInt32 theReadOffset = 0;
	UInt64 theOffset = 0;
	
	// Only if the type is HTTP file transfer, we need to get the File pointer, the Event Context for the read, and the Read Offset into the buffer.
	if ( theTransferType == transferHttpFile ) 
	{
		// File pointer -- Only for Http File transfer type
		(void)QTSS_GetValuePtr(theSession, sFileAttr, 0, (void**)&theFileP, &theLen);
		Assert(theFileP != NULL);
		Assert(theLen == sizeof(QTSS_Object));
		
		(void)QTSS_GetValuePtr(theSession, sReadOffsetAttr, 0, (void**)&theReadOffsetP, &theLen);
		Assert(theReadOffsetP != NULL);
		Assert(theLen == sizeof(UInt32*));
		theReadOffset = *theReadOffsetP;
	
	
		// Get the length of the file onto the stack
		theLen = sizeof(UInt64);
		theErr = QTSS_GetValue(*theFileP, qtssFlObjLength, 0, (void*)&theFileLength, &theLen);
		Assert(theErr == QTSS_NoErr);
		Assert(theLen == sizeof(UInt64));

		// Get the offset in the file onto the stack
		theLen = sizeof(UInt64);	
		theErr = QTSS_GetValue(*theFileP, qtssFlObjPosition, 0, (void*)&theOffset, &theLen);
		Assert(theErr == QTSS_NoErr);
		Assert(theLen == sizeof(UInt64));
	}
	
	Bool16 isBlocked = false;
	while (!isBlocked)
	{
		UInt32 theBufferSize = 0;
		// The buffer is down adjusted to size of the remaining data in the file for Http file transfer
		if( theTransferType == transferHttpFile )
		{
			// If we have less than the full buffer size left to go in the file,
			// down adjust our buffer size to be the amount of data remaining in the file
			theBufferSize = sFileBufSize - theReadOffset;
			if ((theFileLength - theOffset) < theBufferSize)
			theBufferSize = theFileLength - theOffset;
		}
		
		switch (theState)
		{
			case kReadingBufferState:	// Is valid for the Http File transfer case only.
									{
										Assert(theTransferType == transferHttpFile); 		// Just a check
										// Read as much data as possible out of the file
										UInt32 theRecvLen = 0;
										(void)QTSS_Read(*theFileP, (*theFileBufferP) + theReadOffset, theBufferSize, &theRecvLen);
										theReadOffset += theRecvLen;
										theOffset += theRecvLen;
#if HTTP_FILE_DEBUGGING
										printf("Got %lu bytes back from file read. Now at: %"_64BITARG_"u\n", theRecvLen, theOffset);
#endif
										if (theRecvLen < theBufferSize)
										{
#if HTTP_FILE_DEBUGGING
				        					printf("Flow controlled on file. Waiting for read event\n");
#endif
											isBlocked = true;
											break;
										}
				
										theFileBufferLen = theReadOffset;
										(void)QTSS_SetValue(theSession, sFileBufferLenAttr, 0, &theFileBufferLen, sizeof(theFileBufferLen));
										theReadOffset = 0;
										Assert(theWriteOffset == 0);
										theState = kWritingBufferState;
									}

		case kWritingBufferState:	// For both the ref movie directory transfer and the http file transfer
									{
	       								UInt32 theWrittenLen = 0;

	       								(void)QTSS_Write(theSession, (*theFileBufferP) + theWriteOffset, theFileBufferLen - theWriteOffset, &theWrittenLen, 0);
	       								theWriteOffset += theWrittenLen;

#if HTTP_FILE_DEBUGGING
	       								printf("Got %lu bytes back from socket write.\n", theWrittenLen);
#endif
	       								if (theWriteOffset < theFileBufferLen)
	       								{
#if HTTP_FILE_DEBUGGING
		      								printf("Flow controlled on socket.Waiting for write event. \n");
#endif
		      								isBlocked = true;
		      								break;
	       								}
	       
	       								// We have flushed the buffer. Check to see if we are done. 
	       								// If it is Http transfer of the file, we need to check if 
	       								// we read the entire file. If we are, delete stuff and return
										if ((theTransferType == transferHttpFile) && (theOffset == theFileLength))
										{
#if HTTP_FILE_DEBUGGING
											printf("File transfer complete\n");
#endif
											// If we've gotten here, we're done sending the file!
											// Deletion of resources is handled in another role : RTSPSessionClosing role
											// Log the request before returning
											LogRequest(theSession);
											return QTSS_NoErr;
										}
										
										// In the ref movie case, we are done once we flush the buffer.
										if(theTransferType != transferHttpFile)
										{										   
#if HTTP_FILE_DEBUGGING
	       									printf("Refmovie buffer transfer complete\n");
#endif
	       									// Deletion of resources is handled in another role : RTSPSessionClosing role
	       									theWriteOffset = 0;
	       									theState = NULL;
	       									// Log the request before returning
	       									LogRequest(theSession);
	       									return QTSS_NoErr;
										}
										
										// For Http file transfer, we need to set attributes to read the remaining part of the file 
										// since we haven't come to the end of the file yet.
										if ( theTransferType == transferHttpFile )
										{
											theWriteOffset = 0;
											Assert(theReadOffset == 0);
											theState = kReadingBufferState;
										}
									}
		}

	}
	
	Assert(isBlocked);
	
	// We've reached a blocking condition for some reason.
	// Save our state, request an event, and return.
	(void)QTSS_SetValue(theSession, sWriteOffsetAttr, 0, &theWriteOffset, sizeof(theWriteOffset));
	(void)QTSS_SetValue(theSession, sStateAttr, 0, &theState, sizeof(theState));

	// For http file transfer, we need to save the read offset too.
	if( theTransferType == transferHttpFile )
		(void)QTSS_SetValue(theSession, sReadOffsetAttr, 0, &theReadOffset, sizeof(theReadOffset));

	// If we're reading, wait for the file to become readable (valid only for the http file transfer)
	if (theState == kReadingBufferState)
	{
		Assert(theTransferType == transferHttpFile);	// Just a check
		(void)QTSS_RequestEvent(*theFileP, QTSS_ReadableEvent);
	}
	// If we're writing, wait for the socket to become writable (valid for both ref movie and http file transfer)
	else
		(void)QTSS_RequestEvent(theSession, QTSS_WriteableEvent);

	return QTSS_NoErr;
}

QTSS_Error CloseRTSPSession(QTSS_RTSPSession_Params* inParams)
{
	// In this role, the allocated resources are deleted before closing the RTSP session
	QTSS_RTSPSessionObject theSession = inParams->inRTSPSession;
	UInt32 theLen = 0;
	
	// If this feature is disabled just return
	if (!sHTTPFileXferEnabled)
		return QTSS_NoErr;
	
	// Delete the content length string
	char** theContentLengthP = NULL;
	(void)QTSS_GetValuePtr(theSession, sContentLengthAttr, 0, (void**)&theContentLengthP, &theLen);
	if ( theContentLengthP != NULL )
		delete [] *theContentLengthP;
	
	TransferType* theTransferTypeP = NULL;			// Transfer type
	(void)QTSS_GetValuePtr(theSession, sTransferTypeAttr, 0, (void**)&theTransferTypeP, &theLen);
	if (theTransferTypeP != NULL)
	{
	      TransferType theTransferType = *theTransferTypeP;
	  
	     // Get our file buffer pointer and delete it
	     char** theFileBufferP = NULL;					// File buffer pointer
	    (void)QTSS_GetValuePtr(theSession, sFileBufferAttr, 0, (void**)&theFileBufferP, &theLen);
	    if (theFileBufferP != NULL) 
	          delete [] *theFileBufferP;
	
	    QTSS_Object* theFileP = NULL;
	
	   // Only if the type is HTTP file transfer, we need to delete the File pointer and destroy the Event Context
	   if ( theTransferType == transferHttpFile ) 
	   {
		// Get our file pointer and delete it
		(void)QTSS_GetValuePtr(theSession, sFileAttr, 0, (void**)&theFileP, &theLen);
		Assert(theFileP != NULL);
		Assert(theLen == sizeof(QTSS_Object));
		(void)QTSS_CloseFileObject(*theFileP);
	   }
	}
	return QTSS_NoErr;
}

UInt32 GetBitRate(char* filePath) 
{
	UInt32 actualRate = 0, rate = 0;
	QTFile file(false, false);
	QTTrack* track = NULL;
	UInt64 totalRTPBytes = 0;

	QTFile::ErrorCode err = file.Open(filePath);
	if(err != QTFile::errNoError)
		return 0;

	while( file.NextTrack(&track, track) )
	{
		QTHintTrack* hintTrack;
		if( track->Initialize() != QTTrack::errNoError )
		{
			printf("!!! Failed to initialize track !!!\n");
			continue;
		}
	     
	    if( file.IsHintTrack(track) )
	    {
	    	hintTrack = (QTHintTrack*) track;
	    	totalRTPBytes += hintTrack->GetTotalRTPBytes();
	    }
	}

	Float64 duration = file.GetDurationInSeconds();

	if(duration > 0)
		actualRate = (UInt32) ((Float64) (totalRTPBytes * 8) / duration);
       
    if(actualRate <= 14000) 
		rate = 14000;
    else if(actualRate <= 28000)
	    rate = 28000;
    else if(actualRate <= 56000)
	    rate = 56000;
    else if(actualRate <= 112000)
	    rate = 112000;
    else
	    rate = 150000;

#if HTTP_FILE_DEBUGGING
	printf("Actual rate: %lu\n", actualRate);
#endif

    return rate;
}
       
void* MakeMoov(void* rmda, UInt32 rmdaLen, UInt32* moovLen)
{
       UInt32 *rmra, rmraLen, *moov;

       // Make the RMRA
       rmraLen = rmdaLen + 8;
       rmra = NEW UInt32[rmraLen];
       if( rmra == NULL )
	     return NULL;

       rmraLen = htonl(rmraLen);

       ::memcpy(&rmra[0], &rmraLen, 4);
       ::memcpy(&rmra[1], "rmra", 4);
       ::memcpy((char *)rmra + 8, rmda, rmdaLen);

       // Make the MOOV

       *moovLen = ntohl(rmraLen) + 8;
       moov = NEW UInt32[*moovLen];
       if( moov == NULL )
	     return NULL;

       *moovLen = htonl(*moovLen);

       ::memcpy(&moov[0], moovLen, 4);
       ::memcpy(&moov[1], "moov", 4);
       ::memcpy((char *)moov + 8, rmra, ntohl(rmraLen));

       delete rmra;

       *moovLen = ntohl(*moovLen);

       // moov needs to be deleted by the calling function
       return moov;
}

void* MakeRmda(char* url, UInt32 rate, UInt32* rmdaLen) 
{
      UInt32 *rdrf, rdrfLen, *rmdr, rmdrLen, *rmda, zero, size;

      zero = htonl(0); // Okay, this is silly ???
      rate = htonl(rate);

      // Make the RDRF
      size = ::strlen(url) + 1;
      rdrfLen = 20 + size;
      rdrf = NEW UInt32[rdrfLen];
      if( rdrf == NULL )
	     return NULL;

      rdrfLen = htonl(rdrfLen);
      size = htonl(size);

      ::memcpy(&rdrf[0], &rdrfLen, 4);
      ::memcpy(&rdrf[1], "rdrf", 4);
      ::memcpy(&rdrf[2], &zero, 4);
      ::memcpy(&rdrf[3], "url ", 4);
      ::memcpy(&rdrf[4], &size, 4);
      ::strcpy((char *)&rdrf[5], url);

      // Make the RMDR
      rmdrLen = 16;
      rmdr = NEW UInt32[rmdrLen];
      if( rmdr == NULL )
	     return NULL;

      rmdrLen = htonl(rmdrLen);
      
      ::memcpy(&rmdr[0], &rmdrLen, 4);
      ::memcpy(&rmdr[1], "rmdr", 4);
      ::memcpy(&rmdr[2], &zero, 4);
      ::memcpy(&rmdr[3], &rate, 4);

      // Make the RMDA

      *rmdaLen = ntohl(rdrfLen) + ntohl(rmdrLen) + 8;
      rmda = NEW UInt32[*rmdaLen];
      if( rmda == NULL )
	     return NULL;

      *rmdaLen = htonl(*rmdaLen);

      ::memcpy(&rmda[0], rmdaLen, 4);
      ::memcpy(&rmda[1], "rmda", 4);
      ::memcpy((char *)rmda + 8, rmdr, ntohl(rmdrLen));
      ::memcpy((char *)rmda + 8 + ntohl(rmdrLen), rdrf, ntohl(rdrfLen));

      delete rdrf;
      delete rmdr;

      *rmdaLen = ntohl(*rmdaLen);

      // rmda needs to be deleted by the calling function
      return rmda;
}

StrPtrLen* GetMimeType(StrPtrLen* fileName)
{

    StringParser fileNameParser(fileName);
	StrPtrLen str;
	UInt32 suffixSize;
	
	// If the filename doesn't have a suffix, return unknown mime type
	if ( !fileNameParser.GetThru(&str, '.') )
		return &sUnknownMimeType;
			      
	suffixSize = fileName->Len - fileNameParser.GetDataParsedLen();
	StrPtrLen suffix(fileNameParser.GetCurrentPosition(), suffixSize);

	if ( suffix.Equal(sMovSuffix) || suffix.Equal(sQTSuffix) )
		return &sQuickTimeMimeType;

	if( suffix.Equal(sGifSuffix) )
		return &sGifMimeType;

	if( suffix.Equal(sSdpSuffix) )
		return &sSdpMimeType;
	     
	if( suffix.Equal(sSmiSuffix) || suffix.Equal(sSmilSuffix) )
		return &sSmilMimeType;
				
	return &sUnknownMimeType;
}

void LogRequest(QTSS_RTSPSessionObject inRTSPSession)
{
	static StrPtrLen sUnknownStr(sVoidField);
	static char* sStatus = "200";
	UInt32 theLen = 0;
	
	OSMutexLocker locker(sLogMutex);
	CheckHttpAccessLogState(false);
	if (sAccessLog == NULL)				// Http logging is disabled; just return
		return;
		
	// If http logging is on, then log the request... first construct a timestamp
	char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes];
	Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, true);         // date field
	
	//for now, just ignore the error.
	if (!result)
		theDateBuffer[0] = '\0';
	
	// Get info to log. Each attribute must be copied out to ensure that it is NULL terminated.
	// To ensure NULL termination, just memset the buffers to 0, and make sure that
	// the last byte of each array is untouched.
	
	// Get the remost host field
	char remoteAddrBuf[20];	
	StrPtrLen remoteAddr(remoteAddrBuf, 19);	
	if (inRTSPSession != NULL)
		(void)QTSS_GetValue(inRTSPSession, qtssRTSPSesRemoteAddrStr, 0, remoteAddr.Ptr, &remoteAddr.Len);
	
	// Get the request field
	StrPtrLen* theRequestP = NULL;
	(void)QTSS_GetValuePtr(inRTSPSession, sRequestAttr, 0, (void**)&theRequestP, &theLen);
	Assert(theRequestP != NULL);
	Assert(theLen == sizeof(StrPtrLen));
	OSCharArrayDeleter request(NEW char[(*theRequestP).Len + 1]);
	::memcpy(request.GetObject(), (*theRequestP).Ptr, (*theRequestP).Len);
	request.GetObject()[(*theRequestP).Len] = '\0';
	
	// Get the bytes (content length of the document transferred) field
	char** theContentLengthP = NULL;
	char* contentLengthStr = NULL;
	(void)QTSS_GetValuePtr(inRTSPSession, sContentLengthAttr, 0, (void**)&theContentLengthP, &theLen);
	Assert(theContentLengthP != NULL);
	Assert(theLen == sizeof(char*));
	contentLengthStr = *theContentLengthP;
	
	char tempLogBuffer[2048];
	::sprintf(tempLogBuffer, "%s %s %s %s \"%s\" %s %s\n",	
									(remoteAddr.Ptr[0] == '\0') ? sVoidField : remoteAddr.Ptr,
									sVoidField,
									sVoidField,	
									(theDateBuffer[0] == '\0') ? sVoidField : theDateBuffer,	
									(request.GetObject()[0] == '\0') ? sVoidField : request.GetObject(),
									sStatus,
									(contentLengthStr[0] == '\0' ) ? sVoidField : contentLengthStr
									);


	Assert(::strlen(tempLogBuffer) < 2048);
	
	//Write the log message
	sAccessLog->WriteToLog(tempLogBuffer, kAllowLogToRoll);
}

void CheckHttpAccessLogState(Bool16 forceEnabled)
{
	// Code from Access Log module
	// This function makes sure the logging state is in sync with the preferences.
	// extern variable declared in QTSSPreferences.h
	if ((NULL == sAccessLog) && (forceEnabled || sLogEnabled))
	{
		sAccessLog = NEW QTSSHttpAccessLog();
		sAccessLog->EnableLog();
	}

	if ((NULL != sAccessLog) && ((!forceEnabled) && (!sLogEnabled)))
	{
		delete sAccessLog;
		sAccessLog = NULL;
	}
}	

/*
The Common Logfile Format is used for logging all the http requests

format: remotehost rfc931 authuser [date] "request" status bytes

Field Name		Value
remotehost		Remote hostname (or IP number if DNS hostname is not available, or if DNSLookup is Off. 
rfc931			The remote logname of the user. 
authuser		The username as which the user has authenticated himself. 
[date]			Date and time of the request. 
"request"		The request line exactly as it came from the client. 
status			The HTTP status code returned to the client. 
bytes			The content-length of the document transferred. 

*/		
