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

	Contains:	main function to drive streaming server on win32.


*/

#include "getopt.h"
#include "FilePrefsSource.h"

#include "RunServer.h"
#include "QTSServer.h"
#include "QTSSExpirationDate.h"
#include "GenerateXMLPrefs.h"
#include "revision.h"

#define kExecutableName "DarwinStreamingServer"

//
// Data
static FilePrefsSource sPrefsSource(true); // Allow dups
static XMLPrefsParser* sXMLParser = NULL;
static FilePrefsSource sMessagesSource;
static UInt16 sPort = 0; //port can be set on the command line
static int sStatsUpdateInterval = 0;
static SERVICE_STATUS_HANDLE sServiceStatusHandle = 0;
static QTSS_ServerState sInitialState = qtssRunningState;

//
// Functions
static void ReportStatus(DWORD inCurrentState, DWORD inExitCode);
static void InstallService(char* inServiceName);
static void RemoveService(char *inServiceName);
static void RunAsService(char* inServiceName);
void WINAPI ServiceControl(DWORD);
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);


int main(int argc, char * argv[]) 
{
	extern char* optarg;
	
	//First thing to do is to read command-line arguments.
	int ch;
	
	char* theConfigFilePath = "c:\\Program Files\\Darwin Streaming Server\\streamingserver.cfg";
	char* theXMLFilePath = "c:\\Program Files\\Darwin Streaming Server\\streamingserver.xml";
	Bool16 notAService = false;
	Bool16 theXMLPrefsExist = true;
	
	while ((ch = getopt(argc,argv, "vdxp:o:c:irsS:I")) != EOF) // opt: means requires option
	{
		switch(ch)
		{
			case 'v':
				printf("%s/%s Build/%s Platform/%s Built on: %s\n", 	QTSServerInterface::GetServerName().Ptr,
													QTSServerInterface::GetServerVersion().Ptr,
													kBuildString,
													QTSServerInterface::GetServerPlatform().Ptr,
													QTSServerInterface::GetServerBuildDate().Ptr);
                printf("usage: %s [ -d | -p port | -v | -c /myconfigpath.xml | -o /myconfigpath.conf | -x | -S numseconds | -I | -h ]\n", kExecutableName);
				printf("-d: Don't run as a Win32 Service\n");
				printf("-p XXX: Specify the default RTSP listening port of the server\n");
				printf("-c c:\\myconfigpath.xml: Specify a config file path\n");
				printf("-o c:\\myconfigpath.conf: Specify a DSS 1.x / 2.x config file path\n");
				printf("-x: Force create new .xml config file from 1.x / 2.x config\n");
				printf("-i: Install the Darwin Streaming Server service\n");
				printf("-r: Remove the Darwin Streaming Server service\n");
				printf("-s: Start the Darwin Streaming Server service\n");
				printf("-S n: Display server stats in the console every \"n\" seconds\n");
				printf("-I: Start the server in the idle state\n");
                printf("-h: Prints usage\n");				
                ::exit(0);	
			case 'd':
				notAService = true;
				break;
			case 'p':
				Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
				sPort = ::atoi(optarg);
				break;
			case 'c':
				Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
				theXMLFilePath = optarg;
				break;
			case 'S':
				Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
				sStatsUpdateInterval = ::atoi(optarg);
				break;
			case 'o':
				Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
				theConfigFilePath = optarg;
				break;
			case 'x':
				theXMLPrefsExist = false; // Force us to generate a new XML prefs file
				break;
			case 'i':
				printf("Installing the Darwin Streaming Server service...\n");
				::InstallService("Darwin Streaming Server");
				printf("Starting the Darwin Streaming Server service...\n");
				::RunAsService("Darwin Streaming Server");
				::exit(0);
				break;
			case 'r':
				printf("Removing the Darwin Streaming Server service...\n");
				::RemoveService("Darwin Streaming Server");
				::exit(0);
			case 's':
				printf("Starting the Darwin Streaming Server service...\n");
				::RunAsService("Darwin Streaming Server");
				::exit(0);
			case 'I':
				sInitialState = qtssIdleState;
				break;
			default:
				break;
		}
	}
	
	//
	// Check expiration date
	
	QTSSExpirationDate::PrintExpirationDate();
	if (QTSSExpirationDate::IsSoftwareExpired())
	{
		printf("Streaming Server has expired\n");
		::exit(0);
	}

	//
	// Create an XML prefs parser object using the specified path
	sXMLParser = new XMLPrefsParser(theXMLFilePath);
	
	//
	// Check to see if the XML file exists as a directory. If it does,
	// just bail because we do not want to overwrite a directory
	if (sXMLParser->DoesFileExistAsDirectory())
	{
		printf("Directory located at location where streaming server prefs file should be.\n");
		::exit(0);
	}
	
	if (!sXMLParser->CanWriteFile())
	{
		printf("Cannot write to the streaming server prefs file.\n");
		::exit(0);
	}

	// If we aren't forced to create a new XML prefs file, whether
	// we do or not depends solely on whether the XML prefs file exists currently.
	if (theXMLPrefsExist)
		theXMLPrefsExist = sXMLParser->DoesFileExist();
	
	if (!theXMLPrefsExist)
	{
		//
		//Construct a Prefs Source object to get server preferences
		
		int prefsErr = sPrefsSource.InitFromConfigFile(theConfigFilePath);
		if ( prefsErr )
			printf("Could not load configuration file at %s.\n Generating a new prefs file at %s\n", theConfigFilePath, theXMLFilePath);

		//
		// Generate a brand-new XML prefs file out of the old prefs
		int xmlGenerateErr = GenerateAllXMLPrefs(&sPrefsSource, sXMLParser);
		if (xmlGenerateErr)
		{
			printf("Fatal Error: Could not create new prefs file at: %s. (%d)\n", theConfigFilePath, OSThread::GetErrno());
			::exit(-1);
		}		
	}

	//
	// Parse the configs from the XML file
	int xmlParseErr = sXMLParser->Parse();
	if (xmlParseErr)
	{
		printf("Fatal Error: Could not load configuration file at %s. (%d)\n", theXMLFilePath, OSThread::GetErrno());
		::exit(-1);
	}

	//
	// Construct a messages source object
	sMessagesSource.InitFromConfigFile("qtssmessages.txt");

	//
	// Start Win32 DLLs
	WORD wsVersion = MAKEWORD(1, 1);
	WSADATA wsData;
	(void)::WSAStartup(wsVersion, &wsData);
	
	if (notAService)
	{
		// If we're running off the command-line, don't do the service initiation crap.
		::StartServer(sXMLParser, &sMessagesSource, sPort, sStatsUpdateInterval, sInitialState); // No stats update interval for now
		::RunServer();
		::exit(0);
	}
	
    SERVICE_TABLE_ENTRY dispatchTable[] =
    {
        { "", ServiceMain },
        { NULL, NULL }
    };

	//
	// In case someone runs the server improperly, print out a friendly message.
	printf("Darwin Streaming Server must either be started from the DOS Console\n");
	printf("using the -d command-line option, or using the Service Control Manager\n\n");
	printf("Waiting for the Service Control Manager to start Darwin Streaming Server...\n");
	BOOL theErr = ::StartServiceCtrlDispatcher(dispatchTable);
	if (!theErr)
	{
		printf("Fatal Error: Couldn't start Service\n");
		::exit(-1);
	}
}
	
	
void __stdcall ServiceMain(DWORD /*argc*/, LPTSTR *argv)
{
    char* theServerName = argv[0];

	sServiceStatusHandle = ::RegisterServiceCtrlHandler( theServerName, &ServiceControl );
	if (sServiceStatusHandle == 0)
	{
		printf("Failure registering service handler");
        return;
    }

	//
	// Report our status
	::ReportStatus( SERVICE_START_PENDING, NO_ERROR );
	
	//
	// Start & Run the server - no stats update interval for now
	if (::StartServer(sXMLParser, &sMessagesSource, sPort, sStatsUpdateInterval, sInitialState) != qtssFatalErrorState)
	{
		::ReportStatus( SERVICE_RUNNING, NO_ERROR );
		::RunServer(); // This function won't return until the server has died
	
		//
		// Ok, server is done...
		::ReportStatus( SERVICE_STOPPED, NO_ERROR );
	}
	else
		::ReportStatus( SERVICE_STOPPED, ERROR_BAD_COMMAND ); // I dunno... report some error
	
}

void WINAPI ServiceControl(DWORD inControlCode)
{
	QTSS_ServerState theState;
	QTSServerInterface* theServer = QTSServerInterface::GetServer();
	DWORD theStatusReport = SERVICE_START_PENDING;

 	if (theServer != NULL)
		theState = theServer->GetServerState();
	else
		theState = qtssStartingUpState;

   switch(inControlCode)
    {
        // Stop the service.
        //
        case SERVICE_CONTROL_STOP:
        case SERVICE_CONTROL_SHUTDOWN:
        {
 			if (theState == qtssStartingUpState)
 				break;
 				
        	//
        	// Signal the server to shut down.
			theState = qtssShuttingDownState;
			if (theServer != NULL)
				theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState));
            break;
		}
		case SERVICE_CONTROL_PAUSE:
		{
  			if (theState != qtssRunningState)
 				break;
 				
      	 	//
        	// Signal the server to refuse new connections.
			theState = qtssRefusingConnectionsState;
			if (theServer != NULL)
				theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState));
            break;
		}
		case SERVICE_CONTROL_CONTINUE:
		{
  			if (theState != qtssRefusingConnectionsState)
 				break;
 				
        	//
        	// Signal the server to refuse new connections.
			theState = qtssRefusingConnectionsState;
			if (theServer != NULL)
				theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState));
            break;
		}
		case SERVICE_CONTROL_INTERROGATE:
			break; // Just update our status
			
        default:
            break;
    }

 	if (theServer != NULL)
	{
		theState = theServer->GetServerState();
		
		//
		// Convert a QTSS state to a Win32 Service state
		switch (theState)
		{
			case qtssStartingUpState:			theStatusReport = SERVICE_START_PENDING;	break;
			case qtssRunningState:				theStatusReport = SERVICE_RUNNING; 			break;
			case qtssRefusingConnectionsState:	theStatusReport = SERVICE_PAUSED; 			break;
			case qtssFatalErrorState:			theStatusReport = SERVICE_STOP_PENDING; 	break;
			case qtssShuttingDownState:			theStatusReport = SERVICE_STOP_PENDING; 	break;
			default:							theStatusReport = SERVICE_RUNNING; 			break;
		}
	}
	else
		theStatusReport = SERVICE_START_PENDING;

	printf("Reporting status from ServiceControl function\n");
	::ReportStatus(theStatusReport, NO_ERROR);
}

void ReportStatus(DWORD inCurrentState, DWORD inExitCode)
{
    static Bool16 sFirstTime = 1;
    static UInt32 sCheckpoint = 0;
	static SERVICE_STATUS sStatus;
    
    if(sFirstTime)
    {
        sFirstTime = false;
        
		//
		// Setup the status structure
		sStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
		sStatus.dwCurrentState = SERVICE_START_PENDING;
		//sStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
		sStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
		sStatus.dwWin32ExitCode = 0;
		sStatus.dwServiceSpecificExitCode = 0;
		sStatus.dwCheckPoint = 0;
		sStatus.dwWaitHint = 0;
    }

	if (sStatus.dwCurrentState == SERVICE_START_PENDING)
		sStatus.dwCheckPoint = ++sCheckpoint;
	else
		sStatus.dwCheckPoint = 0;
	
	sStatus.dwCurrentState = inCurrentState;
	sStatus.dwServiceSpecificExitCode = inExitCode;
	BOOL theErr = SetServiceStatus(sServiceStatusHandle, &sStatus);
	if (theErr == 0)
	{
		DWORD theerrvalue = ::GetLastError();
	}
}

void RunAsService(char* inServiceName)
{
	SC_HANDLE   theService;
	SC_HANDLE   theSCManager;
	
	theSCManager = ::OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS   // access required
                        );
	if (!theSCManager)
		return;

    theService = ::OpenService(
        theSCManager,               // SCManager database
        inServiceName,               // name of service
        SERVICE_ALL_ACCESS );

	SERVICE_STATUS lpServiceStatus;

	if (theService)
	{	const SInt32 kNotRunning = 1062;
		Bool16 stopped = ::ControlService(theService, SERVICE_CONTROL_STOP, &lpServiceStatus);
		if(!stopped && ( (SInt32) ::GetLastError() != kNotRunning) )
			printf("Stopping Service Error: %d\n", ::GetLastError());

		Bool16 started = ::StartService(theService, 0, NULL);
		if(!started)
			printf("Starting Service Error: %d\n", ::GetLastError());
		
		::CloseServiceHandle(theService);
	}

	::CloseServiceHandle(theSCManager);
}


void InstallService(char* inServiceName)
{
    SC_HANDLE   theService;
    SC_HANDLE   theSCManager;

    TCHAR thePath[512];
    TCHAR theQuotedPath[522];

    BOOL theErr = ::GetModuleFileName( NULL, thePath, 512 );
    if (!theErr)
    	return;

    ::sprintf(theQuotedPath, "\"%s\"", thePath);

    theSCManager = ::OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS   // access required
                        );
	if (!theSCManager)
	{
		printf("Failed to install Darwin Streaming Server Service\n");
		return;
	}

    theService = CreateService(
        theSCManager,               // SCManager database
        inServiceName,               // name of service
        inServiceName,               // name to display
        SERVICE_ALL_ACCESS,         // desired access
        SERVICE_WIN32_OWN_PROCESS,  // service type
        SERVICE_AUTO_START,       // start type
        SERVICE_ERROR_NORMAL,       // error control type
        theQuotedPath,               // service's binary
        NULL,                       // no load ordering group
        NULL,                       // no tag identifier
        NULL,       // dependencies
        NULL,                       // LocalSystem account
        NULL);                      // no password

	if (theService)
	{
		::CloseServiceHandle(theService);
		printf("Installed Darwin Streaming Server Service\n");
	}
	else
		printf("Failed to install Darwin Streaming Server Service\n");

	::CloseServiceHandle(theSCManager);
}

void RemoveService(char *inServiceName)
{
    SC_HANDLE   theSCManager;
    SC_HANDLE   theService;

    theSCManager = ::OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS   // access required
                        );
	if (!theSCManager)
	{
		printf("Failed to remove Darwin Streaming Server Service\n");
		return;
	}

    theService = ::OpenService(theSCManager, inServiceName, SERVICE_ALL_ACCESS);
	if (theService != NULL)
	{
		Bool16 stopped = ::ControlService(theService, SERVICE_CONTROL_STOP, NULL);
		if(!stopped)
			printf("Stopping Service Error: %d\n", ::GetLastError());

		(void)::DeleteService(theService);
		::CloseServiceHandle(theService);
		printf("Removed Darwin Streaming Server Service\n");
    }
    else
		printf("Failed to remove Darwin Streaming Server Service\n");

	::CloseServiceHandle(theSCManager);
}
