//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
//
// Windows CE SMB file server driver componet
//
#include <CReg.hxx>
#include <String.hxx>
#include <service.h>
#include <GUIDGen.h>
#include <svsutil.hxx>
#include <strsafe.h>

#include "SMB_Globals.h"
#include "utils.h"
#include "NetbiosTransport.h"
#include "TCPTransport.h"
#include "ShareInfo.h"
#include "PrintQueue.h"
#include "PktHandler.h"
#include "FileServer.h"
#include "ConnectionManager.h"
#include "SMBIOCTL.h"
#include <psl_marshaler.hxx>
#include "wdm.h"
#include "pc_net_prog.h"

#ifndef  SECURITY_WIN32
#define  SECURITY_WIN32
#endif
#include <security.h>
#include <sspi.h>
#include "serverAdmin.h"

extern SERVERNAME_HARDENING_LEVEL SmbServerNameHardeningLevel;

class InitClass {
public:
    InitClass() {
        static BOOL fInited = FALSE;
        if(FALSE == fInited) {
            #ifdef DEBUG
                SMB_Globals::g_lMemoryCurrentlyUsed = 0;
            #endif

            svsutil_Initialize();
            fInited = TRUE;
        } else {
            ASSERT(FALSE);
        }
    }
    ~InitClass() {
        svsutil_DeInitialize();
    }
};


// Stream interface functions
#define SMB_CONTEXT  0xABCDEF01

//
// Global that will call into SVS Utils init etc (any construction that must happen first)
InitClass g_InitClass;
ThreadSafePool<10, SMB_PACKET>   SMB_Globals::g_SMB_Pool;
BOOL      g_fServerRunning = FALSE;

CRITICAL_SECTION g_csDriverLock;
extern HRESULT StartListenOnNameChange();
extern VOID SMB_RestartServer();

HRESULT CreateShare(const WCHAR *pName,
                    DWORD dwType,
                    const WCHAR *pPath,
                    const WCHAR *pACL,
                    const WCHAR *pROACL,
                    const WCHAR *pDriver,
                    const WCHAR *pComment);


#define TOASCII(x, dest) \
    { \
        dest = NULL; \
        UINT uiSize; \
        if(0 != (uiSize = WideCharToMultiByte(CP_ACP, 0, x, -1, NULL, 0,NULL,NULL))) \
        { \
            __try { \
                dest = (char *)_alloca(uiSize); \
                \
                WideCharToMultiByte(CP_ACP, 0, x, -1, dest, uiSize, NULL, NULL); \
            } __except(1) { \
                dest = NULL; \
            } \
        } \
    }




HRESULT AddFileShare(const WCHAR *_pName,
                 const WCHAR *_pPath,
                 const WCHAR *_pACL,
                 const WCHAR *_pROACL)
{
    ASSERT(TRUE == g_fFileServer);
    HRESULT hr = E_FAIL;
    Share *pFileShare = NULL;

    //
    // Verify incoming params
    if(NULL == _pName ||
       NULL == _pPath) {
       TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - Invalid Params")));
       ASSERT(FALSE);
       goto Done;
    }


    //add file as a share
    if(NULL == (pFileShare = new Share())) {
        hr = E_OUTOFMEMORY;
        goto Done;
    }

    pFileShare->SetDirectoryName(_pPath);
    pFileShare->SetShareName(_pName);
    pFileShare->SetShareType(STYPE_DISKTREE);
    pFileShare->SetServiceName(L"A:");
    pFileShare->SetACL(_pACL, _pROACL);
    pFileShare->SetRequireAuth(TRUE);


    if(FAILED(hr = SMB_Globals::g_pShareManager->AddShare(pFileShare))) {
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - StartServer failed -- cant add default share!")));
        delete pFileShare;
        goto Done;
    }

    hr = S_OK;
    Done:
        return hr;
}



HRESULT AddPrintSpool(const  WCHAR *_pName,
                   const WCHAR *_pDriverName,
                   const WCHAR *_pProc,
                   const WCHAR *_pComment,
                   const WCHAR *_pDirectoryName,
                   const WCHAR *_pShareName,
                   const WCHAR *_pServiceName,
                   const WCHAR *_pRemark,
                   const WCHAR *_pPortName,
                   const WCHAR *_pACL)

{
    SMBPrintQueue *pPrintQueue;
    Share *pPrinterShare;
    HRESULT hr = E_FAIL;

    CHAR *pName;
    CHAR *pDriverName;
    CHAR *pProc;
    CHAR *pComment;
    CHAR *pRemark;

    TOASCII(_pName, pName);
    TOASCII(_pDriverName, pDriverName);
    TOASCII(_pProc, pProc);
    TOASCII(_pComment, pComment);
    TOASCII(_pRemark, pRemark);

    if(NULL == _pName ||
       NULL == _pDriverName ||
       NULL == _pProc ||
       NULL == _pComment ||
       NULL == _pRemark) {
       TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - Error doing ASCII conversion")));
       goto Done;
    }

    //

    if(NULL == (pPrintQueue = new SMBPrintQueue())) {
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - StartServer failed -- couldnt create printqueue")));
        hr = E_OUTOFMEMORY;
        goto Done;
    }

    //
    // Add all printers connected to the mix
    pPrintQueue->SetStartTime(SecSinceJan1970_0_0_0());
    pPrintQueue->SetName(pName);
    pPrintQueue->SetDriverName(pDriverName);
    pPrintQueue->SetPrProc(pProc);
    pPrintQueue->SetSepFile("");
    pPrintQueue->SetParams("");
    pPrintQueue->SetComment(pComment);
    pPrintQueue->SetPriority(1);
    pPrintQueue->SetPortName(_pPortName);


    //add \printer as a share
    if(NULL == (pPrinterShare = new Share())) {
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - StartServer failed -- couldnt create shares")));
        hr = E_OUTOFMEMORY;
        goto Done;
    }

    pPrinterShare->SetDirectoryName(_pDirectoryName);
    pPrinterShare->SetShareName(_pShareName);
    pPrinterShare->SetServiceName(_pServiceName);
    pPrinterShare->SetRemark(pRemark);
    pPrinterShare->SetShareType(STYPE_PRINTQ);
    pPrinterShare->SetPrintQueue(pPrintQueue);
    pPrinterShare->SetRequireAuth(TRUE);
    pPrinterShare->SetACL(_pACL, L"");

    if(FAILED(hr = SMB_Globals::g_pShareManager->AddShare(pPrinterShare))) {
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - StartServer failed -- cant add default share!")));
    }

    hr = S_OK;
    Done:
        return hr;
}


HRESULT AddShare(const WCHAR *ShareName)
{
    HRESULT hr = E_FAIL;
    CReg ShareKey;
    WCHAR Path[MAX_PATH];
    TRACEMSG(ZONE_INIT, (L"Loading Share: %s", ShareName));
    WCHAR RegPath[MAX_PATH];
    WCHAR RegDriver[MAX_PATH];
    WCHAR RegComment[MAX_PATH];
    WCHAR ACL[MAX_PATH];
    WCHAR ROACL[MAX_PATH];
    DWORD dwType;
    DWORD dwRegType;
    DWORD dwLen = sizeof(DWORD);

    if(!ShareName) {
        RETAILMSG(1, (L"  -- Share Name NULL\n"));
        goto Done;
    }

    hr = StringCbPrintf(Path, MAX_PATH*sizeof(WCHAR), L"Services\\SMBServer\\Shares\\%s", ShareName);
    if(FAILED(hr)) {
        RETAILMSG(1, (L"  -- Failed to combine %s into Services\\SMBServer\\Shares\\%s", ShareName, ShareName));
        goto Done;
    }


    RETAILMSG(1, (L"SMB_PRINT: Loading Registry: %s\n", Path));
    if(FALSE == ShareKey.Open(HKEY_LOCAL_MACHINE, Path)) {
        RETAILMSG(1, (L"   -- Cant Open KEY %s\n", Path));
        goto Done;
    }

    //
    // Get the type
    if(ERROR_SUCCESS != RegQueryValueEx(ShareKey, L"Type", 0, &dwRegType, (LPBYTE)&dwType, &dwLen) ||
       REG_DWORD != dwRegType ||
       sizeof(DWORD) != dwLen) {
        RETAILMSG(1, (L"   -- Cant Open TYPE on %s\n", Path));
        goto Done;
    }
    RETAILMSG(1, (L"   -- Type: %d\n", dwType));


    //
    // Only let file shares in if we are linked up with the file server
    //    code -- otherwise just do print
    if(FALSE == (g_fFileServer && dwType == STYPE_DISKTREE) &&
       dwType != STYPE_PRINTQ) {
        RETAILMSG(1, (L"   -- unknown type!! -- we only support printers"));
        goto Done;
    }

    //
    // Get the Path
    if(FALSE == ShareKey.ValueSZ(L"Path", RegPath, MAX_PATH/sizeof(WCHAR))) {
        RETAILMSG(1, (L"   -- Cant get PATH\n"));
        goto Done;
    }
    RETAILMSG(1, (L"   -- Path: %s\n", RegPath));

    //
    // Get the ACL
    if(FALSE == ShareKey.ValueSZ(L"UserList", ACL, MAX_PATH/sizeof(WCHAR))) {
        RETAILMSG(1, (L"    --  NOT USING ACL's!\n"));
        ACL[0] = NULL;
    }
    RETAILMSG(1, (L"   -- ACL: %s\n", ACL));

    if(FALSE == ShareKey.ValueSZ(L"ROUserList", ROACL, MAX_PATH/sizeof(WCHAR))) {
        RETAILMSG(1, (L"    --  NOT USING READ-ONLY ACL's!\n"));
        ROACL[0] = NULL;
    }
    RETAILMSG(1, (L"   -- Read-ONLYACL: %s\n", ROACL));


    //
    // Get printer specific information
    if(dwType == STYPE_PRINTQ)
    {
        //
        // Get the Driver (printer specific)
        if(FALSE == ShareKey.ValueSZ(L"Driver", RegDriver, MAX_PATH/sizeof(WCHAR))) {
            RETAILMSG(1, (L"   -- Cant get Driver\n"));
            RegDriver[0] = NULL;
        }
        RETAILMSG(1, (L"   -- Driver: %s\n", RegDriver));

        //
        // Get the Comment (printer specific)
        if(FALSE == ShareKey.ValueSZ(L"Comment", RegComment, MAX_PATH/sizeof(WCHAR))) {
            RETAILMSG(1, (L"   -- Cant get Comment\n"));
            RegComment[0] = 0;
        }
        RETAILMSG(1, (L"   -- Comment: %s\n", RegComment));


        //
        // Load up the driver
        AddPrintSpool(ShareName,
                       RegDriver,
                       L"WinPrint",
                       RegComment,
                       L"PRINTER_DIR",
                       ShareName,
                       L"LPT1:",
                       RegComment,
                       RegPath,
                       ACL);

    }else if (dwType == STYPE_DISKTREE) {
       //
       //  Add the fileshare
       ASSERT(TRUE == g_fFileServer);
       AddFileShare(ShareName, RegPath, ACL, ROACL);
    }

    hr = S_OK;
    Done:
        return hr;
}

HRESULT AddShares()
{
    CReg RegShares;
    WCHAR ShareName[MAX_PATH];
    BOOL fFoundSomething = FALSE;
    Share *pIPCShare = NULL;

    //
    // Add IPC Share
    if(NULL == (pIPCShare = new Share())) {
        return E_OUTOFMEMORY;
    }
    pIPCShare->SetDirectoryName(L"IPC$");
    pIPCShare->SetShareName(L"IPC$");
    pIPCShare->SetServiceName(L"IPC");
    pIPCShare->SetRequireAuth(FALSE); //let anyone in
    pIPCShare->SetRemark("Remote Inter Process Communication");
    pIPCShare->SetShareType(STYPE_IPC);
    pIPCShare->SetPrintQueue(NULL);

    if(FAILED(SMB_Globals::g_pShareManager->AddShare(pIPCShare))) {
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - StartServer failed -- cant add IPC$ share!")));
        goto Done;
    }


    //
    // Load the CName from the registry
    if(FALSE == RegShares.Open(HKEY_LOCAL_MACHINE, L"Services\\SMBServer\\Shares")) {
        RETAILMSG(1, (L"No registy key under Services\\SMBServer\\Shares"));
    }

    while(TRUE == RegShares.EnumKey(ShareName, MAX_PATH)) {
        if(SUCCEEDED(AddShare(ShareName))) {
            fFoundSomething = TRUE;
        }
    }

    Done:
    if(fFoundSomething)
        return S_OK;
    else
        return E_FAIL;
}

DWORD
SMB_Init(
    DWORD dwContext
    )
{
    DWORD dwRet = 0;
    CReg CRCName;
    CReg CRSettings;
    CReg RegAllowAll;
    DWORD dwType;
    DWORD dwLen;
    DWORD dwSetting;
    WCHAR MyCName[16];
    WCHAR OrigName[16];
    WCHAR wszWorkGroup[MAX_PATH];
    BOOL fAllowAll = FALSE;

    TRACEMSG(ZONE_INIT,(TEXT("+SMB_Init\n")));
    UINT i;


    //
    // Print a RETAIL message if passwords are disabled
    if(TRUE == RegAllowAll.Open(HKEY_LOCAL_MACHINE, L"Services\\SMBServer\\Shares")) {
        fAllowAll = !(RegAllowAll.ValueDW(L"UseAuthentication", TRUE));
        if(TRUE == fAllowAll) {
           RETAILMSG(1, (L"SMB_SRV: **PASSWORDS DISABLED**"));
        }
    }

    //
    // Initialize any globals
    //
    // First get the server GUID from registry (if it doesnt exist, create it)
    if(FALSE == CRSettings.OpenOrCreateRegKey(HKEY_LOCAL_MACHINE,
                                              L"Services\\SMBServer",0)) {
     TRACEMSG(ZONE_ERROR, (L"SMB_SRVR: Error opening up SMBPrint reg key!\n"));
        dwRet = 0;
        goto Done;
    }

    if(FALSE == CRSettings.ValueSZ(L"AdapterList", SMB_Globals::g_AdapterAllowList, MAX_PATH / sizeof(WCHAR))) {
     TRACEMSG(ZONE_ERROR, (L"Name value under HKLM\\Services\\SMBServer\\AdapterList is not there\n"));
        SMB_Globals::g_AdapterAllowList[0] =  NULL;
    }

    dwLen = sizeof(SMB_Globals::g_ServerGUID);
    if(ERROR_SUCCESS != RegQueryValueEx(CRSettings,
                                         L"GUID",
                                         NULL,
                                         &dwType,
                                         SMB_Globals::g_ServerGUID,
                                         &dwLen) || (16 != dwLen) || (REG_BINARY != dwType))
    {
        GUID srvrGUID;
        TRACEMSG(ZONE_INIT, (L"SMB_SRV: Server GUID not specified in registry -- creating a new one"));
        GenerateGUID(&srvrGUID);

        memcpy(&(SMB_Globals::g_ServerGUID[0]), (BYTE *)(&srvrGUID.Data1), 4);
        memcpy(&(SMB_Globals::g_ServerGUID[4]), (BYTE *)(&srvrGUID.Data2), 2);
        memcpy(&(SMB_Globals::g_ServerGUID[6]), (BYTE *)(&srvrGUID.Data3), 2);
        memcpy(&(SMB_Globals::g_ServerGUID[8]),  srvrGUID.Data4, 8);

        if(TRUE != CRSettings.SetBinary(L"GUID", SMB_Globals::g_ServerGUID, sizeof(SMB_Globals::g_ServerGUID))) {
            TRACEMSG(ZONE_ERROR, (L"SMB_SRV: Cant write GUID to registry -- we will continue, but this isnt good"));
            ASSERT(FALSE);
        }
    }

    if (ERROR_SUCCESS != RegQueryValueEx(
                                      CRSettings,
                                      L"SmbServerNameHardeningLevel",
                                      NULL,
                                      &dwType,
                                      (LPBYTE)&dwSetting,
                                      &dwLen))
    {
        TRACEMSG(ZONE_INIT, (L"SMB_SRV: Name value under HKLM\\Services\\SMBServer\\SmbServerNameHardeningLevel is not there."));
    TRACEMSG(ZONE_INIT, (L"SMB_SRV: Server hardening level is not specified in registry -- using NO HARDENED."));

    SmbServerNameHardeningLevel = SmbServerNameNoCheck;
    
    }
    else
    {
         if ((dwSetting == SmbServerNamePartialCheck) ||
        (dwSetting == SmbServerNameFullCheck))
         {
            SmbServerNameHardeningLevel = (SERVERNAME_HARDENING_LEVEL)dwSetting;
         }
    }

    InitializeCriticalSection(&SMB_Globals::SrvAdminNameLock);
    
    if(FALSE == CRSettings.ValueSZ(L"workgroup", wszWorkGroup, sizeof(wszWorkGroup)/sizeof(WCHAR))) {
        RETAILMSG(1, (L"SMB_SRV: Workgroup reg value not set -- using WORKGROUP\n"));
        sprintf(SMB_Globals::g_szWorkGroup, "WORKGROUP");
    } else {
        BYTE *pWorkGroup = NULL;
        UINT uiSize;
        StringConverter SCWorkGroup;
        SCWorkGroup.append(wszWorkGroup);
        pWorkGroup = SCWorkGroup.NewSTRING(&uiSize, FALSE);
        if(NULL == pWorkGroup) {
            dwRet = 0;
            ASSERT(FALSE);
            goto Done;
        }
        strcpy(SMB_Globals::g_szWorkGroup, (CHAR *)pWorkGroup);
        LocalFree(pWorkGroup);
    }


    //
    // Set MaxConnections
    SMB_Globals::g_uiMaxConnections = CRSettings.ValueDW(L"MaxConnections", 5);


#ifdef DEBUG
    SMB_Globals::g_PacketID = 0;
#endif

    //
    // Make sure the netbios transport is initialized (because we may start listening for name changes etc)
    InitNetbiosTransport();


    //
    // Load the CName from the registry
    if(FALSE == CRCName.Open(HKEY_LOCAL_MACHINE, L"Ident")) {
        RETAILMSG(1, (L"No registy key under HKLM\\Ident"));
        dwRet = SMB_CONTEXT;
        goto Done;
    }
    if(FALSE == CRCName.ValueSZ(L"Name", MyCName, sizeof(MyCName)/sizeof(WCHAR))) {
        RETAILMSG(1, (L"Name value under HKLM\\Ident is either too large (15 chars) or isnt present -- stopping server"));
        dwRet = SMB_CONTEXT;
        goto Done;
    }
    if(FALSE == CRCName.ValueSZ(L"OrigName", OrigName, sizeof(OrigName)/sizeof(WCHAR))) {
        RETAILMSG(1, (L"OrigName value under HKLM\\Ident is either too large (15 chars) or isnt present -- stopping server"));
        dwRet = SMB_CONTEXT;
        goto Done;
    }
    if(0 == wcscmp(MyCName, OrigName)) {
        RETAILMSG(1, (L"OrigName = Name value under HKLM\\Ident -- stopping server to prevent multiple machine with the same name"));
        dwRet = SMB_CONTEXT;

        //
        // In the case where names arent the same start up the name listener so if they become okay we will start again
        StartListenOnNameChange();
        goto Done;
    }

    if(0 == WideCharToMultiByte(CP_ACP, 0, MyCName, -1, SMB_Globals::CName, sizeof(SMB_Globals::CName),NULL,NULL)) {
        TRACEMSG(ZONE_ERROR, (L"Conversion of CName (%s) failed!!!", MyCName));
        ASSERT(FALSE);
        dwRet = 0;
        goto Done;
    }
    //make sure its upper case
    for(i=0; i<sizeof(SMB_Globals::CName); i++) {
        SMB_Globals::CName[i] = toupper(SMB_Globals::CName[i]);
    }

    RETAILMSG(1, (L"SMB_SRV: Registering CName %s", MyCName));

    //
    // Create a abstract filesystem (since CE doesnt have locking)
    SMB_Globals::g_pAbstractFileSystem = new AbstractFileSystem();
    if(NULL == SMB_Globals::g_pAbstractFileSystem) {
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - StartServer failed -- couldnt create abstract file system")));
        dwRet = 0;
        goto Done;
    }

    //
    // Start the master share list
    SMB_Globals::g_pShareManager = new ShareManager();
    if(NULL == SMB_Globals::g_pShareManager) {
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - StartServer failed -- couldnt create shares")));
        dwRet = 0;
        goto Done;
    }

    //
    // Start the TID Manager
    SMB_Globals::g_pConnectionManager = new ConnectionManager();
    if(NULL == SMB_Globals::g_pConnectionManager) {
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - StartServer failed -- couldnt create connection manager")));
        dwRet = 0;
        goto Done;
    }

    //
    // Load (and add) all shares from the registry
    if(FAILED(AddShares())) {
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - adding shares failed!")));
        dwRet = SMB_CONTEXT;
        goto NoShares;
    }

    //
    // Start up the packet PktHandler
    if(FAILED(StartPktHandler())) {
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - StartServer failed")));
        dwRet = 0;
        goto Done;
    }

    //
    // Start up the print spool
    if(FAILED(StartPrintSpool())) {
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - StartPrintSpool failed")));
        dwRet = 0;
        goto Done;
    }

    //
    // Start up protocol transports
    if(FAILED(StartTCPTransport())) {  //TCP must be started BEFORE Netbios!
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - StartServer on TCP failed")));
        ASSERT(FALSE);
        dwRet = 0;
        goto Done;
    }
    if(FAILED(StartNetbiosTransport())) {
        TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init - StartServer on NETBsdIOS failed")));
        ASSERT(FALSE);
        dwRet = 0;
        goto Done;
    }

    dwRet = SMB_CONTEXT;
    g_fServerRunning = TRUE;
    Done:
        if(0 == dwRet) {
    NoShares:
                TRACEMSG(ZONE_ERROR, (TEXT("SMB_Init -- an error occured during init... cleaning up")));

                //
                // Stop transports (NOTE: this is okay to do even if they were not inited!!
                if(FAILED(StopTCPTransport())) {
                    TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init -- couldnt stop TCP transport!!")));
                    ASSERT(FALSE);
                }
                if(FAILED(StopNetbiosTransport())) {
                    TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init -- couldnt stop NETBIOS transport!!")));
                    ASSERT(FALSE);
                }

                //
                // Stop print spool
                if(FAILED(StopPrintSpool())) {
                    TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init -- couldnt stop print spool!!")));
                    ASSERT(FALSE);
                }

                //
                // Stop the PktHandler
                if(FAILED(StopPktHandler())) {
                    TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init -- couldnt stop PktHandler!!")));
                    ASSERT(FALSE);
                }

                //
                // Clean up memory
                if(NULL != SMB_Globals::g_pShareManager) {
                    delete SMB_Globals::g_pShareManager;
                    SMB_Globals::g_pShareManager = NULL;
                }
                if(NULL != SMB_Globals::g_pConnectionManager) {
                    delete SMB_Globals::g_pConnectionManager;
                    SMB_Globals::g_pConnectionManager = NULL;
                }
                if(NULL != SMB_Globals::g_pWakeUpOnEvent) {
                    delete SMB_Globals::g_pWakeUpOnEvent;
                    SMB_Globals::g_pWakeUpOnEvent = NULL;
                }
                if(NULL != SMB_Globals::g_pAbstractFileSystem) {
                    delete SMB_Globals::g_pAbstractFileSystem;
                    SMB_Globals::g_pAbstractFileSystem = NULL;
                }
        }

        TRACEMSG(ZONE_INIT,(TEXT("-SMB_Init")));
        return dwRet;
}


DWORD
SMB_Deinit(
    DWORD dwClientContext
    )
{
    TRACEMSG(ZONE_INIT,(TEXT("+SMB_Deinit")));
    BOOL fSuccess = TRUE;

    //
    // Halt all existing TCP connections (and dont allow for new ones)
    if(FAILED(HaltTCPIncomingConnections())) {
        TRACEMSG(ZONE_INIT, (TEXT("-SMB_Deinit - Closing TCP transport incoming connections failed")));
        fSuccess = FALSE;
        ASSERT(FALSE);
    }

    //
    // Halt all existing NETBIOS connections (and dont allow for new ones)
    if(FAILED(StopNetbiosTransport())) {
        TRACEMSG(ZONE_INIT, (TEXT("-SMB_Deinit - Closing NETBIOS transport failed")));
        fSuccess = FALSE;
        ASSERT(FALSE);
    }

    //
    // Stop the PktHandler -- this will flush all existing packets
    TRACEMSG(ZONE_INIT, (TEXT("-SMB_Deinit - Halt SMB PktHandler")));
    if(FAILED(StopPktHandler())) {
        TRACEMSG(ZONE_INIT, (L"-SMB_Deinit -- error stopping PktHandler!!"));
        fSuccess = FALSE;
        ASSERT(FALSE);
    }

    //
    // Now shut off the transport threads
    TRACEMSG(ZONE_INIT, (TEXT("-SMB_Deinit - Halting TCP transport")));
    if(FAILED(StopTCPTransport())) {
        TRACEMSG(ZONE_INIT, (TEXT("-SMB_Deinit - Closing TCP transport failed")));
        fSuccess = FALSE;
        ASSERT(FALSE);
    }



    TRACEMSG(ZONE_INIT, (TEXT("-SMB_Deinit - Deleting memory")));

    //
    // Delete all connection information
    //   The ShareManager needs to be around still!
    if(SMB_Globals::g_pConnectionManager) {
        delete SMB_Globals::g_pConnectionManager;
        SMB_Globals::g_pConnectionManager = NULL;
    }

    //
    // Stop print spool
    if(FAILED(StopPrintSpool())) {
       TRACEMSG(ZONE_ERROR, (TEXT("-SMB_Init -- couldnt stop print spool!!")));
       ASSERT(FALSE);
    }

    //
    // Delete all share information (print spool must be down by now
    //   because the share binds to the print spool and blocks on its termination)
    if(SMB_Globals::g_pShareManager) {
       delete SMB_Globals::g_pShareManager;
       SMB_Globals::g_pShareManager = NULL;
    }

    //
    // Delete wakeup event object iff the namechange notification handle isnt alive
    if(SMB_Globals::g_pWakeUpOnEvent && !NETBIOS_TRANSPORT::NameChangeNotification::h) {
        delete SMB_Globals::g_pWakeUpOnEvent;
        SMB_Globals::g_pWakeUpOnEvent = NULL;
    }

    //
    // Delete the abstract file system
    if(NULL != SMB_Globals::g_pAbstractFileSystem) {
        delete SMB_Globals::g_pAbstractFileSystem;
        SMB_Globals::g_pAbstractFileSystem = NULL;
    }

    //
    // Delete the printers memmapped buffers
    if(FAILED(SMB_Globals::g_PrinterMemMapBuffers.Close())) {
        TRACEMSG(ZONE_ERROR, (L"SMB_PRINT: Error closing swap buffer!"));
        ASSERT(FALSE);
    }

    //
    // Destroy any netbios memory
    DestroyNetbiosTransport();

    DeleteCriticalSection(&SMB_Globals::SrvAdminNameLock);

    g_fServerRunning = FALSE;
    TRACEMSG(ZONE_INIT,(TEXT("-SMB_Deinit")));
    return fSuccess;
}


DWORD
SMB_Open(
    DWORD dwClientContext,
    DWORD dwAccess,
    DWORD dwShareMode
    )
{
    TRACEMSG(ZONE_INIT,(TEXT("+SMB_Open")));
    return SMB_CONTEXT;
}


BOOL
SMB_Close(
    DWORD dwOpenContext
    )
{
    TRACEMSG(ZONE_INIT,(TEXT("+SMB_Close")));
    return 0;
}


DWORD
SMB_Read(
    DWORD dwOpenContext,
    LPVOID pBuffer,
    DWORD dwNumBytes
    )
{
    TRACEMSG(ZONE_INIT,(TEXT("+SMB_Read")));
    return 0;
}


DWORD
SMB_Write(
    DWORD dwOpenContext,
    LPCVOID pBuffer,
    DWORD dwNumBytes
    )
{
    TRACEMSG(ZONE_INIT,(TEXT("+SMB_Write")));
    return 0;
}


DWORD
SMB_Seek(
    DWORD dwOpenContext,
    long lDistance,
    DWORD dwMoveMethod
    )
{
    TRACEMSG(ZONE_INIT,(TEXT("+SMB_Seek")));
    return 0;
}

void SMB_PowerUp(void)  {return;}
void SMB_PowerDown(void){return;}

BOOL IOCTL_Change_ACL(ce::marshal_arg<ce::copy_in,PCWSTR> pShareName, ce::marshal_arg<ce::copy_in,PCWSTR> pACL, ce::marshal_arg<ce::copy_in,PCWSTR> pROACL)
{
    CReg ShareKey;
    HRESULT hr = E_FAIL;

    StringConverter ShareName;
    ShareName.append(L"Services\\SMBServer\\Shares\\");
    ShareName.append(pShareName);

    if(FALSE == ShareKey.Open(HKEY_LOCAL_MACHINE, ShareName.GetString())) {
        RETAILMSG(1, (L"SMBSERVER: Error -- cant change ACL because %s isnt a valid share", pShareName));
        goto Done;
    }
    if(pACL && FALSE == ShareKey.SetSZ(L"UserList", pACL, wcslen(pACL))) {
        RETAILMSG(1, (L"SMBSERVER: Error -- cant change ACL because %s userlist cant be set", pShareName));
        goto Done;
    }
    if(pROACL && FALSE == ShareKey.SetSZ(L"ROUserList", pROACL, wcslen(pROACL))) {
        RETAILMSG(1, (L"SMBSERVER: Error -- cant change ACL because %s ROUserList cant be set", pShareName));
        goto Done;
    }

    if(TRUE == g_fServerRunning && NULL != SMB_Globals::g_pShareManager) {
        SMB_Globals::g_pShareManager->ReloadACLS();
    }

    hr = S_OK;
    Done:
    if (FAILED(hr)) {
        SetLastError(hr);
        return FALSE;
    }
    return TRUE;
}

BOOL IOCTL_Add_Share(ce::marshal_arg<ce::copy_in,PCWSTR> pName, DWORD dwType, ce::marshal_arg<ce::copy_in,PCWSTR> pPath, ce::marshal_arg<ce::copy_in,PCWSTR> pACL, ce::marshal_arg<ce::copy_in,PCWSTR> pROACL, ce::marshal_arg<ce::copy_in,PCWSTR> pDriver, ce::marshal_arg<ce::copy_in,PCWSTR> pComment)
{
    HRESULT hr;
    CReg regPaths;

    if(0 != wcsstr(pPath, L"..")) {
        hr = E_FAIL;
        goto Done;
    }

    if(regPaths.OpenOrCreateRegKey(HKEY_LOCAL_MACHINE, L"Services\\SMBServer\\ExcludePaths")) {
        WCHAR wcName[100];
        WCHAR wcPath[500];

        while(regPaths.EnumValue(wcName, sizeof(wcName)/sizeof(WCHAR), wcPath, sizeof(wcPath))) {
            if(0 == _memicmp(pPath, wcPath, wcslen(wcPath))) {
                RETAILMSG(1, (L"SMBSERVER: Error adding share with path name %s because that path is blocked", pPath));
                hr = E_FAIL;
                goto Done;
            }
        }
    }

    if(SUCCEEDED(hr = CreateShare(pName, dwType, pPath, pACL, pROACL, pDriver, pComment))) {
        if(FALSE == g_fServerRunning) {
            SMB_RestartServer();
        } else {
            AddShare(pName);
        }
    }

    Done:
    if (FAILED(hr)) {
        SetLastError(hr);
        return FALSE;
    }
    return TRUE;
}

BOOL IOCTL_Del_Share(ce::marshal_arg<ce::copy_in,PCWSTR> pName)
{
    // NOTE: its safe to search for this share and use it w/o CritSection b/c we are the
    //   only person that ever deletes shares
    Share *pMyShare = NULL;

    if(g_fServerRunning) {
        pMyShare = SMB_Globals::g_pShareManager->SearchForShare((WCHAR*)((const WCHAR*)pName));
    }

    ce::wstring RegPath = L"Services\\SMBServer\\Shares\\";
    RegPath.append((WCHAR *)((const WCHAR*)pName));
    HRESULT hr = E_FAIL;

    if(g_fServerRunning && NULL != pMyShare) {
            if(SUCCEEDED(SMB_Globals::g_pConnectionManager->TerminateTIDsOnShare(pMyShare))) {
               SMB_Globals::g_pShareManager->DeleteShare(pMyShare);
               if(ERROR_SUCCESS != RegDeleteKey(HKEY_LOCAL_MACHINE, RegPath)) {
                   RETAILMSG(1, (L"SMBSRV:  Share %s doesnt exist in the registry!", (WCHAR *)(RegPath.get_buffer())));
               }
               hr = S_OK;
            }
    } else if(!g_fServerRunning) {
      if(ERROR_SUCCESS != RegDeleteKey(HKEY_LOCAL_MACHINE, RegPath)) {
          RETAILMSG(1, (L"SMBSRV:  Share %s doesnt exist in the registry!", (WCHAR *)(RegPath.get_buffer())));
      } else {
          SMB_RestartServer();
          hr = S_OK;
      }
    }

    if (FAILED(hr)) {
        SetLastError(hr);
        return FALSE;
    }
    return TRUE;
}

BOOL IOCTL_List_Connected_Users(ce::marshal_arg<ce::copy_out, ce::psl_buffer_wrapper<PWSTR> > pBuffer,
                                   ce::marshal_arg<ce::copy_out, PDWORD> puiLen)
{
  ce::wstring WhosOn = L"";

  if(g_fServerRunning && SMB_Globals::g_pConnectionManager) {
      SMB_Globals::g_pConnectionManager->ListConnectedUsers(WhosOn);
  }
  *puiLen = sizeof(WCHAR) * (WhosOn.length()+1);

  if(pBuffer.count() < (WhosOn.length()+1)) {
      SetLastError(E_OUTOFMEMORY);
      return FALSE;
  }
  memcpy(pBuffer.buffer(), WhosOn.get_buffer(),sizeof(WCHAR) * (WhosOn.length()+1));
  return TRUE;
}

BOOL IOCTL_QueryAmountTransfered(ce::marshal_arg<ce::copy_out, LARGE_INTEGER*> pRead,
                                    ce::marshal_arg<ce::copy_out, LARGE_INTEGER*> pWritten)
{
  CCritSection csLock(&SMB_Globals::g_Bookeeping_CS);
  csLock.Lock();
  pRead->QuadPart = SMB_Globals::g_Bookeeping_TotalRead.QuadPart;
  pWritten->QuadPart = SMB_Globals::g_Bookeeping_TotalWritten.QuadPart;
  return TRUE;
}

BOOL IOCTL_NetUpdateAliases(ce::marshal_arg<ce::copy_in, PCWSTR> pBuffer,
                                    DWORD uiLen)
                                    
/*++

   Routine Description:

       used to update server administration aliases list.

   Arguments:

          pBuffer -names need to be saved in server administration aliases list. it should be formated as name1\0name2\0name3
          uiLen - length of pBuffer in byte.

   Return Values:

          TRUE/FALSE.

   Notes:

       None.

--*/

{
    UNICODE_STRING Aliases = {0};
    UNICODE_STRING localHost = {18 ,18, L"LocalHost"};
    NTSTATUS status;

    if (uiLen == 0)
    {
        return FALSE;
    }

    Aliases.Buffer = (PWCHAR)(const WCHAR*)pBuffer;
    Aliases.Length = Aliases.MaximumLength = uiLen;

    status = SrvAdminAllocateNameList(
                    &Aliases,
                    &localHost,
                    &SMB_Globals::SrvAdminNameLock,
                    &SMB_Globals::SrvAdminServerNameList
                    );

    if (STATUS_SUCCESS == status)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

BOOL IOCTL_NetUpdateIpAddress(ce::marshal_arg<ce::copy_in, PCWSTR> pBuffer,
                                        DWORD uiLen)
/*++

   Routine Description:

      Used to update server administration aliases list.

   Arguments:

      pBuffer - names need to be saved in server administration Ip address list. It should be formated as name1\0name2\0name3
      uiLen - length of pBuffer in byte.

   Return Values:

      TRUE/FALSE

   Notes:

      None.

 --*/
{
    UNICODE_STRING IPAddr = {0};
    NTSTATUS status;

    if (uiLen == 0)
    {
        return FALSE;
    }

    IPAddr.Buffer = (PWCHAR)(const WCHAR*)pBuffer;
    IPAddr.Length = IPAddr.MaximumLength = uiLen;

    status = SrvAdminAllocateNameList(
                                &IPAddr,
                                NULL,
                                &SMB_Globals::SrvAdminNameLock,
                                &SMB_Globals::SrvAdminIpAddressList
                                );

    if (STATUS_SUCCESS == status)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

BOOL
SMB_IOControl(
    DWORD dwOpenContext,
    DWORD dwIoControlCode,
    PBYTE pInBuf,
    DWORD nInBufSize,
    PBYTE pOutBuf,
    DWORD nOutBufSize,
    PDWORD pBytesReturned
    )
{
    BOOL fRet = 1;
    CCritSection csLock(&g_csDriverLock);
    csLock.Lock();


    switch(dwIoControlCode) {
        case IOCTL_SERVICE_REFRESH:
            SMB_RestartServer();
            break;
        case SMB_IOCTL_INVOKE: {
            ce::psl_stub<> stub(pInBuf, nInBufSize);

            switch(stub.function()) {
                case IOCTL_CHANGE_ACL:
                    fRet = stub.call(IOCTL_Change_ACL);
                    break;
                case IOCTL_ADD_SHARE:
                    fRet = stub.call(IOCTL_Add_Share);
                    break;
                case IOCTL_DEL_SHARE:
                    fRet = stub.call(IOCTL_Del_Share);
                    break;
                case IOCTL_LIST_USERS_CONNECTED:
                    fRet = stub.call(IOCTL_List_Connected_Users);
                    break;
                case IOCTL_QUERY_AMOUNT_TRANSFERED:
                    fRet = stub.call(IOCTL_QueryAmountTransfered);
                    break;
           case IOCTL_NET_UPDATE_ALIASES:
            fRet = stub.call(IOCTL_NetUpdateAliases);
            break;
           case IOCTL_NET_UPDATE_IP_ADDRESS:
            fRet = stub.call(IOCTL_NetUpdateIpAddress);
            break;
            }
            break;
        }
      /*  case IOCTL_SET_MAX_CONNS:
            if(sizeof(UINT) == nInBufSize) {
                CReg reg;
                if(reg.OpenOrCreateRegKey(HKEY_LOCAL_MACHINE, L"Services\\SMBServer")) {
                   SMB_Globals::g_uiMaxConnections = *((UINT *)pInBuf);
                   reg.SetDW(L"MaxConnections", SMB_Globals::g_uiMaxConnections);
                   TRACEMSG(ZONE_INIT, (L"SMB_SRV: Set Max Connections to: %d", SMB_Globals::g_uiMaxConnections));
                } else {
                    TRACEMSG(ZONE_ERROR, (L"SMB_SRV: SMB registry not present!"));
                    ASSERT(FALSE);
                }
            }
            break;
        */

        case IOCTL_SERVICE_STOP:
            TRACEMSG(ZONE_INIT,(L"+SMB_IOControl, STOP IOCTL"));
            SMB_Deinit(0);
            break;
        case IOCTL_SERVICE_START:
            TRACEMSG(ZONE_INIT,(L"+SMB_IOControl, START IOCTL"));
            SMB_Init(0);
            break;
        #ifdef DEBUG
        case IOCTL_DEBUG_PRINT:
            SMB_Globals::g_pConnectionManager->DebugPrint();
            break;
        #endif
        default:
            TRACEMSG(ZONE_INIT,(TEXT("+SMB_IOControl, unknown code 0x%X"),dwIoControlCode));
            break;
    }
    return fRet; //0=failure, nonzero=success
}


DWORD SMBSRVR_RestartServerThread(LPVOID netnum)
{
    CCritSection csLock(&g_csDriverLock);
    csLock.Lock();

    if (TRUE == SMB_Deinit(0))
    {
        TRACEMSG(ZONE_DETAIL, (TEXT("SMBSRV-RESTARTTHREAD: SMB_Deinit succeeded\r\n")));
    }
    else
    {
        TRACEMSG(ZONE_DETAIL, (TEXT("SMBSRV-RESTARTTHREAD: SMB_Deinit failed\r\n")));
    }    
    
    if (SMB_CONTEXT == SMB_Init(0))
    {
        TRACEMSG(ZONE_DETAIL, (TEXT("SMBSRV-RESTARTTHREAD: SMB_Init succeeded\r\n")));
    }
    else
    {
        TRACEMSG(ZONE_DETAIL, (TEXT("SMBSRV-RESTARTTHREAD: SMB_Init failed\r\n")));
    }

    csLock.UnLock();
    return 0;
}


VOID SMB_RestartServer()
{
    HANDLE h;
    if(NULL == (h = CreateThread(NULL, 0, SMBSRVR_RestartServerThread, 0, 0, NULL))) {
        TRACEMSG(ZONE_ERROR, (L"SMBSRV-PktHandler: cant make restart thread"));
        ASSERT(FALSE);
        return;
    }
    CloseHandle(h);
}

extern "C"
BOOL WINAPI DllMain(IN HANDLE DllHandle,
                    IN ULONG Reason,
                    IN PVOID Context OPTIONAL)
{
    switch(Reason) {

        case DLL_PROCESS_ATTACH:
            TRACEMSG(ZONE_INIT,(TEXT("SMBSRV: DLL_PROCESS_ATTACH, hInst:0x%X"), DllHandle));
            InitializeCriticalSection(&g_csDriverLock);
            DEBUGREGISTER((HINSTANCE)DllHandle);
            SMB_Globals::g_Bookeeping_TotalRead.QuadPart = 0;
            SMB_Globals::g_Bookeeping_TotalWritten.QuadPart = 0;
            InitializeCriticalSection(&SMB_Globals::g_Bookeeping_CS);
            return TRUE;

        case DLL_PROCESS_DETACH:
            TRACEMSG(ZONE_INIT,(TEXT("SMBSRV: DLL_PROCESS_DETACH")));
            DeleteCriticalSection(&g_csDriverLock);
            DeleteCriticalSection(&SMB_Globals::g_Bookeeping_CS);
            return TRUE;

        case DLL_THREAD_ATTACH:    // A new thread has been created
        case DLL_THREAD_DETACH:    // Thread has exited
            // No processing for these
            return TRUE;

        default:
            TRACEMSG(ZONE_ERROR,(TEXT("SMBSRV:DllEntry: Invalid reason #%d"), Reason));
            return FALSE;
            break;
    }
    return TRUE;
}


HRESULT CreateShare(const WCHAR *pName,
                    DWORD dwType,
                    const WCHAR *pPath,
                    const WCHAR *pACL,
                    const WCHAR *pROACL,
                    const WCHAR *pDriver,
                    const WCHAR *pComment)
{
    ce::wstring RegPath = L"Services\\SMBServer\\Shares\\";
    HRESULT hr = E_FAIL;
    CReg reg;

    if(NULL == pName) {
        goto Done;
    }

    RegPath += pName;
    if(FALSE == reg.OpenOrCreateRegKey(HKEY_LOCAL_MACHINE, RegPath)) {
        goto Done;
    }

    //
    // These values are for all share typessd
    if(FALSE == reg.SetDW(L"Type", dwType)) {
        goto Done;
    }
    if(NULL != pPath && FALSE == reg.SetSZ(L"Path", pPath)) {
        goto Done;
    }
    if(NULL != pACL && FALSE == reg.SetSZ(L"UserList", pACL)) {
       goto Done;
    }
    if(NULL != pROACL && FALSE == reg.SetSZ(L"ROUserList", pROACL)) {
       goto Done;
    }

    if(dwType == STYPE_PRINTQ) {
        if(NULL != pDriver && FALSE == reg.SetSZ(L"Driver", pDriver)) {
            goto Done;
        }
        if(NULL != pComment && FALSE == reg.SetSZ(L"Comment", pComment)) {
           goto Done;
        }
    } else if(dwType == STYPE_DISKTREE) {
    } else {
        RETAILMSG(1, (L"SMBSRV: Error Creating Share! unknown type!"));
        ASSERT(FALSE);
        goto Done;
    }

    hr = S_OK;
    Done:
        return hr;
}

