/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    ts3.c

Abstract:

    This is a test program for exercising the service controller.  This
    program acts like a service and exercises the Service Controller API
    that can be called from a service:
        SetServiceStatus
        StartServiceCtrlDispatcher   
        RegisterServiceCtrlHandler

Author:

    Dan Lafferty (danl)      2 Apr-1992

Environment:

    User Mode -Win32

Revision History:

--*/

//
// Includes
//

#include <nt.h>      // DbgPrint prototype
#include <ntrtl.h>      // DbgPrint prototype
#include <nturtl.h>     // needed for winbase.h

#include <windows.h>

#include <winsvc.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>      // OpenFile
#include <sys\types.h>  // OpenFile
#include <sys\stat.h>   // OpenFile
#include <io.h>         // OpenFile

#include <tstr.h>       // Unicode string macros
#include <rpc.h>

//
// Defines
//

#define INFINITE_WAIT_TIME  0xffffffff

#define NULL_STRING     TEXT("");


//
// Globals
//

    SERVICE_STATUS  SingleStatus;

    HANDLE          SingleDoneEvent;

    SERVICE_STATUS_HANDLE   SingleStatusHandle;

//
// Function Prototypes
//

VOID
SingleStart (
    DWORD   argc,
    LPTSTR  *argv
    );


VOID
SingleCtrlHandler (
    IN  DWORD   opcode
    );

DWORD
GetIntlFormat(
    LPWSTR  type,
    LPWSTR  string,
    DWORD   numChars);

VOID
GetTime(
    LPWSTR  *time
    );


/****************************************************************************/
VOID __cdecl
main(void)
{
    DWORD   status;

    SERVICE_TABLE_ENTRY   DispatchTable[] = {
        { TEXT("single"),   SingleStart     },
        { TEXT("single1"),  SingleStart     },   // this entry should be ignored.
        { NULL,             NULL            }
    };
    
    if (!StartServiceCtrlDispatcher( DispatchTable)) {
        status = GetLastError();
        DbgPrint("[ts3]StartServiceCtrlDispatcher failed %d \n",status);
        if (status = ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
            printf("Failed to connect to service controller, this "
            "program should be started with the Services Control Panel Applet, "
            "or at the command line with Net Start <ServiceName>");
        }
    }

    DbgPrint("[ts3]The Service Process is Terminating....)\n");

    ExitProcess(0);

}


/****************************************************************************/

//
// Single will take a long time to respond to pause
//
//

VOID
SingleStart (
    DWORD   argc,
    LPTSTR  *argv
    )
{
    DWORD   status;
    DWORD   i;
    NETRESOURCEW netResource;

    DbgPrint(" [SINGLE] Inside the Single Service Thread\n");

    for (i=0; i<argc; i++) {
        DbgPrint(" [SINGLE] CommandArg%d = %s\n", i,argv[i]);
    }


    SingleDoneEvent = CreateEvent (NULL, TRUE, FALSE,NULL);

    //
    // Fill in this services status structure
    //
    DbgPrint(" [SINGLE] Send status with ServiceType = SERVICE_WIN32\n"
             "          This should not overwrite the copy that SC maintains\n"
             "          which should be SERVICE_WIN32_OWN_PROCESS\n");

    SingleStatus.dwServiceType        = SERVICE_WIN32;
    SingleStatus.dwCurrentState       = SERVICE_RUNNING;
    SingleStatus.dwControlsAccepted   = SERVICE_ACCEPT_STOP |
                                        SERVICE_ACCEPT_PAUSE_CONTINUE;
    SingleStatus.dwWin32ExitCode      = 0;
    SingleStatus.dwServiceSpecificExitCode = 0;
    SingleStatus.dwCheckPoint         = 0;
    SingleStatus.dwWaitHint           = 0;
    
    //
    // Register the Control Handler routine.
    //

    DbgPrint(" [SINGLE] Getting Ready to call RegisterServiceCtrlHandler\n");

    SingleStatusHandle = RegisterServiceCtrlHandler(
                            TEXT("single"),
                            SingleCtrlHandler);

    if (SingleStatusHandle == (SERVICE_STATUS_HANDLE)0) {
        DbgPrint(" [SINGLE] RegisterServiceCtrlHandler failed %d\n", GetLastError());
    }
    
    //
    // Return the status
    //

    if (!SetServiceStatus (SingleStatusHandle, &SingleStatus)) {
        status = GetLastError();
        DbgPrint(" [SINGLE] SetServiceStatus error %ld\n",status);
    }

    //================================
    // SPECIAL TEST GOES HERE.
    //================================

#define TEST_USE_ADD
#ifdef TEST_USE_ADD

    netResource.lpRemoteName = L"\\\\Kernel\\scratch";
    netResource.lpLocalName  = L"z:";
    netResource.lpProvider = NULL;
    netResource.dwType = RESOURCETYPE_DISK;

    status = WNetAddConnection2W(&netResource, NULL, NULL, 0L);
    if (status != NO_ERROR) {
        DbgPrint("WNetAddConnection (z:) Failed %d\n",status);
    }

    netResource.lpRemoteName = L"\\\\popcorn\\public";
    netResource.lpLocalName  = L"p:";
    netResource.lpProvider = NULL;
    netResource.dwType = RESOURCETYPE_DISK;

    status = WNetAddConnection2W(&netResource, NULL, NULL, 0L);
    if (status != NO_ERROR) {
        DbgPrint("WNetAddConnection (p:) Failed %d\n",status);
    }
#endif

    {
        UUID        Uuid;
        RPC_STATUS  rpcstatus;

        rpcstatus = UuidCreate(&Uuid);
        if (rpcstatus != NO_ERROR) {
            DbgPrint("UuidCreate Failed %d \n",rpcstatus);
        }
    }

    //
    // Wait forever until we are told to terminate.
    //
    {

        //
        // This portion of the code determines that the working directory
        // is the system32 directory.
        //
        LPSTR   String = GetEnvironmentStrings();
        DWORD   rc;

        Sleep(1000);
        while (*String != 0) {
            DbgPrint("%s\n",String);
            String += (strlen(String) + 1);
        }
        rc = _open("DansFile.txt",O_CREAT | O_BINARY,S_IREAD | S_IWRITE);
        if (rc == -1) {
            DbgPrint("OpenFile Failed\n");
        }
            
    }

    status = WaitForSingleObject (
                SingleDoneEvent,
                INFINITE_WAIT_TIME);

    status = WNetCancelConnectionW(L"z:",FALSE);
    if (status != NO_ERROR) {
        DbgPrint("WNetCancelConnection (z:) Failed %d\n",status);
    }
    status = WNetCancelConnectionW(L"p:",FALSE);
    if (status != NO_ERROR) {
        DbgPrint("WNetCancelConnection (p:) Failed %d\n",status);
    }

    DbgPrint(" [SINGLE] Leaving the single service\n");

    ExitThread(NO_ERROR);
    return;
}


/****************************************************************************/
VOID
SingleCtrlHandler (
    IN  DWORD   Opcode
    )
{

    DWORD   status;
    LPWSTR  time;

    HANDLE          enumHandle;
    DWORD           numElements;
    DWORD           bufferSize;
    LPNETRESOURCE   pNetResource;
    DWORD           i;

    DbgPrint(" [SINGLE] opcode = %ld\n", Opcode);

    //
    // Find and operate on the request.
    //

    switch(Opcode) {
    case SERVICE_CONTROL_PAUSE:

        DbgPrint("[SINGLE] Sleep 1 minute before responding to pause request\n");
        Sleep(60000);    // 1 minute

        SingleStatus.dwCurrentState = SERVICE_PAUSED;
        break;

    case SERVICE_CONTROL_CONTINUE:

        SingleStatus.dwCurrentState = SERVICE_RUNNING;
        break;

    case SERVICE_CONTROL_STOP:

        SingleStatus.dwWin32ExitCode = 0;
        SingleStatus.dwCurrentState = SERVICE_STOPPED;

        SetEvent(SingleDoneEvent);
        break;

    case SERVICE_CONTROL_INTERROGATE:
        status = WNetOpenEnumW(
                    RESOURCE_CONNECTED,
                    RESOURCETYPE_DISK,
                    0,
                    NULL,
                    &enumHandle);

        if (status != WN_SUCCESS) {
            DbgPrint("WNetOpenEnum failed %d\n",status);
        }
        else {
            //
            // Attempt to allow for 10 connections
            //
            bufferSize = (10*sizeof(NETRESOURCE))+1024;
        
            pNetResource = (LPNETRESOURCE) LocalAlloc(LPTR, bufferSize);

            if (pNetResource == NULL) {
                DbgPrint("TestEnum:LocalAlloc Failed %d\n",GetLastError);
                break;
            }
            numElements = 0xffffffff;
            status = WNetEnumResourceW(
                            enumHandle,
                            &numElements,
                            pNetResource,
                            &bufferSize);

            if ( status != WN_SUCCESS) {
                DbgPrint("WNetEnumResource failed %d\n",status);
        
                //
                // If there is an extended error, display it.
                //
                if (status == WN_EXTENDED_ERROR) {
                    DbgPrint("Extended Error\n");
                }
                WNetCloseEnum(enumHandle);
                LocalFree(pNetResource);
            }
            else {
                if (numElements == 0) {
                    DbgPrint("No Connections to Enumerate\n");
                }
                for (i=0; i < numElements ;i++ ) {
                    DbgPrint("%ws is connected to %ws\n",
                    pNetResource[i].lpLocalName,
                    pNetResource[i].lpRemoteName);

                }
                WNetCloseEnum(enumHandle);
                LocalFree(pNetResource);
            }
        }
        GetTime(&time);
        DbgPrint(" [SINGLE] time = %ws\n",time);
        break;

    default:
        DbgPrint(" [SINGLE] Unrecognized opcode %ld\n", Opcode);
    }

    //
    // Send a status response.
    //

    if (!SetServiceStatus (SingleStatusHandle,  &SingleStatus)) {
        status = GetLastError();
        DbgPrint(" [SINGLE] SetServiceStatus error %ld\n",status);
    }
    return;    
}

//************************************************************************
//
// TEST CODE
//
//************************************************************************
#define PARSE_SIZE      80
#define TIME_SEP_SIZE    2

VOID
GetTime(
    LPWSTR  *time
    )
{
    WCHAR       czParseString[PARSE_SIZE];
    WCHAR       czTimeString[PARSE_SIZE];
    LPWSTR      pCurLoc;
    LPWSTR      pTime;
    DWORD       numChars;
    SYSTEMTIME  SysTime;
    LPWSTR      AMPMString=L"";
    WCHAR       TimeSep[TIME_SEP_SIZE];
    BOOL        TwelveHour=TRUE;
    BOOL        LeadingZero=FALSE;
    DWORD       i,dateType;
    DWORD       numSame;

    //-----------------------------------------
    // Get the Current Time and Date.
    //-----------------------------------------
    GetLocalTime(&SysTime);

#ifdef CL_DEBUG
    printf("Year=%d,Month=%d,Day=%d,Hour=%d,Minute=%d\n",
        SysTime.wYear,
        SysTime.wMonth,
        SysTime.wDay,
        SysTime.wHour,
        SysTime.wMinute);
#endif
    //-----------------------------------------
    // Get the Date Format  (M/d/yy)
    //-----------------------------------------
    numChars = GetIntlFormat(L"sShortDate",czParseString,PARSE_SIZE);
    if (numChars == 0) {
        //
        // No data, use the default.
        //
        wcscpy(czParseString, L"M/d/yy");
    }

    //-----------------------------------------
    // Fill in the date string
    //-----------------------------------------
    pCurLoc = czTimeString;

    for (i=0; i<numChars; i++ ) {

        dateType = i;
        numSame  = 1;

        //
        // Find out how many characters are the same.
        // (MM or M, dd or d, yy or yyyy)
        //
        while (czParseString[i] == czParseString[i+1]) {
            numSame++;
            i++;
        }

        //
        // i is the offset to the last character in the date type.
        //

        switch (czParseString[dateType]) {
        case L'M':
        case L'm':
            //
            // If we have a single digit month, but require 2 digits,
            // then add a leading zero.
            //
            if ((numSame == 2) && (SysTime.wMonth < 10)) {
                *pCurLoc = L'0';
                pCurLoc++;
            }
            ultow(SysTime.wMonth, pCurLoc, 10);
            pCurLoc += wcslen(pCurLoc);
            break;

        case L'D':
        case L'd':

            //
            // If we have a single digit day, but require 2 digits,
            // then add a leading zero.
            //
            if ((numSame == 2) && (SysTime.wDay < 10)) {
                *pCurLoc = L'0';
                pCurLoc++;
            }
            ultow(SysTime.wDay, pCurLoc, 10);
            pCurLoc += wcslen(pCurLoc);
            break;

        case L'Y':
        case L'y':

            ultow(SysTime.wYear, pCurLoc, 10);
            //
            // If we are only to show 2 digits, take the
            // 3rd and 4th, and move them into the first two
            // locations.
            //
            if (numSame == 2) {
                pCurLoc[0] = pCurLoc[2];
                pCurLoc[1] = pCurLoc[3];
                pCurLoc[2] = L'\0';
            }
            pCurLoc += wcslen(pCurLoc);
            break;

        default:
            printf("Default case: Unrecognized time character - "
            "We Should never get here\n");
            break;
        }
        //
        // Increment the index beyond the last character in the data type.
        // If not at the end of the buffer, add the separator character.
        // Otherwise, add the trailing NUL.
        //
        i++;
        if ( i<numChars ) {
            *pCurLoc = czParseString[i];
            pCurLoc++;
        }
        else {
            *pCurLoc='\0';
        }
    }

    //-----------------------------------------
    // 12 or 24 hour format?
    //-----------------------------------------
    numChars = GetIntlFormat(L"iTime",czParseString,PARSE_SIZE);
    if (numChars > 0) {
        if (*czParseString == L'1'){
            TwelveHour = FALSE;
        }
    }

    //-----------------------------------------
    // Is there a Leading Zero?
    //-----------------------------------------
    if (GetProfileIntW(L"intl",L"iTLZero",0) == 1) {
        LeadingZero = TRUE;
    }

    //-----------------------------------------
    // Get the Time Separator character.
    //-----------------------------------------
    numChars = GetIntlFormat(L"sTime",TimeSep,TIME_SEP_SIZE);
    if (numChars == 0) {
        //
        // No data, use the default.
        //
        TimeSep[0] = L':';
        TimeSep[1] = L'\0';
    }

    //-------------------------------------------------
    // If running a 12 hour clock, Get the AMPM string.
    //-------------------------------------------------
    if (TwelveHour) {
        if (SysTime.wHour > 11) {
            numChars = GetIntlFormat(L"s2359",czParseString,PARSE_SIZE);
        }
        else {
            numChars = GetIntlFormat(L"s1159",czParseString,PARSE_SIZE);
        }
        if (numChars > 0) {
            AMPMString = LocalAlloc(LMEM_FIXED,wcslen(czParseString)+sizeof(WCHAR));
            if (AMPMString != NULL) {
                wcscpy(AMPMString,czParseString);
            }
        }
    }

    //
    // Build the time string
    //
    pTime = czTimeString + (wcslen(czTimeString) + 1);

    if ((TwelveHour) && (SysTime.wHour > 12)) {
        SysTime.wHour -= 12;
    }
    //
    // If the time is a single digit, and we need a leading zero,
    // than add the leading zero.
    //
    if ((SysTime.wHour < 10) && (LeadingZero)) {
        *pTime = L'0';
        pTime++;
    }
    ultow(SysTime.wHour, pTime, 10);
    pTime += wcslen(pTime);
    *pTime = *TimeSep;
    pTime++;
    if (SysTime.wMinute < 10) {
        *pTime = L'0';
        pTime++;
    }
    ultow(SysTime.wMinute, pTime, 10);
    wcscat(pTime,AMPMString);

    pTime = czTimeString + (wcslen(czTimeString) + 1);

#ifdef CL_DEBUG
    printf("Time = %ws,  Date = %ws\n",pTime,czTimeString);
#endif

    *(--pTime) = L' ';
    printf("\n  %ws\n", czTimeString);
    *time = czTimeString;
}

DWORD
GetIntlFormat(
    LPWSTR  type,
    LPWSTR  string,
    DWORD   numChars)
{
    DWORD num;

    num = GetProfileStringW(L"intl",type,L"",string,numChars);

#ifdef CL_DEBUG
    if (num > 0) {
        printf("%ws string from ini file = %ws\n",type, string);
    }
    else {
        printf("%ws string from ini file = (empty)\n",type);
    }
#endif
    return(num);
}