//
// 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.
//
#include <CReg.hxx>
#include <authhlp.h>

#include "SMB_Globals.h"
#include "ShareInfo.h"
#include "CriticalSection.h"
#include "encrypt.h"
#include "PrintQueue.h"
#include "FileServer.h"
#include "ConnectionManager.h"
#ifdef SMB_NMPIPE
#include "ipcstream.h"
#endif


//
// Just used to make sure (in debug) that multiple instances of ShareManager/TIDManager
//   dont get created
#ifdef DEBUG
        BOOL ShareManager::fFirstInst = TRUE;
#endif

UniqueID                           TIDState::UIDTidGenerator;
UniqueID                           TIDState::UniqueFID;
ce::fixed_block_allocator<30>      SMB_Globals::g_FileStreamAllocator;
ce::fixed_block_allocator<10>      SMB_Globals::g_PrintStreamAllocator;

Share::Share() {

    pShareName = NULL;
    pShareNameSZ = NULL;

    pRemark = NULL;
    fHidden = FALSE;
    shareType = 0xFF;

    pPrintQueue = NULL;
    fRequireAuth = TRUE;
}

Share::~Share() {
    if(pShareName)
        delete [] pShareName;
    if(pShareNameSZ)
        delete [] pShareNameSZ;
    if(pRemark)
        delete [] pRemark;
    if(pPrintQueue) {
        delete pPrintQueue;
    }
}


HRESULT
Share::SetPrintQueue(SMBPrintQueue *_pPrintQueue)
{
    ASSERT(NULL == pPrintQueue);
    pPrintQueue = _pPrintQueue;

    return S_OK;
}

SMBPrintQueue *
Share::GetPrintQueue()
{
    return pPrintQueue;
}

HRESULT
Share::SetRemark(const CHAR *_pRemark)
{
    HRESULT hr=S_OK;;
    UINT uiRemarkLen;

    if(NULL == _pRemark) {
        hr = E_INVALIDARG;
        goto Done;
    }
    if(NULL != pRemark) {
        delete [] pRemark;
    }

    uiRemarkLen = strlen(_pRemark) + 1;
    pRemark = new CHAR[uiRemarkLen];

    if(NULL == pRemark) {
        hr = E_OUTOFMEMORY;
        goto Done;
    }
    memcpy(pRemark, _pRemark, uiRemarkLen);

    Done:
        return hr;

}




HRESULT
Share::SetShareName(const WCHAR *_pShareName)
{
    HRESULT hr = S_OK;
    WCHAR *pNewShareName = NULL;
    CHAR *pNewShareNameSZ = NULL;
    UINT uiSize = 0;
    UINT uiMultiSize = 0;

    //
    // Verify that the sharename is not null or empty string
    if(!_pShareName || !_pShareName[0]) {
        TRACEMSG(ZONE_ERROR, (L"SMB_SRV: ERROR: Sharename must not be null or empty string."));
        ASSERT(FALSE);
        return E_INVALIDARG;
    }

    uiSize = wcslen(_pShareName) + 1;
    uiMultiSize = WideCharToMultiByte(CP_ACP, 0, _pShareName, -1, NULL, NULL, NULL, NULL);

    //
    // Share name must be <= 13 characters total (with null)
    if(uiSize > 13) {
        TRACEMSG(ZONE_ERROR, (L"SMB_SRV: ERROR: Sharename must be <= 13 characters with null"));
        ASSERT(FALSE);
        return E_INVALIDARG;
    }

    pNewShareName = new WCHAR[uiSize];
    pNewShareNameSZ = new CHAR[uiMultiSize];

    if(NULL == pNewShareName || NULL == pNewShareNameSZ) {
        hr = E_OUTOFMEMORY;
        goto Done;
    }


    WideCharToMultiByte(CP_ACP, 0, _pShareName, -1, pNewShareNameSZ, uiMultiSize,NULL,NULL);
    wcscpy(pNewShareName, _pShareName);


    if(pShareName)
        delete [] pShareName;
    if(pShareNameSZ)
        delete [] pShareNameSZ;

    pShareName = pNewShareName;
    pShareNameSZ = pNewShareNameSZ;

    Done:
        if(FAILED(hr)) {
           if(pNewShareName)
                delete [] pNewShareName;
           if(pNewShareNameSZ)
                delete [] pNewShareNameSZ;
        }

        return hr;
}




HRESULT
Share::SetDirectoryName(const WCHAR *_pDirectory)
{
    m_DirectoryName.Clear();
    return m_DirectoryName.append(_pDirectory);
}

HRESULT
Share::SetServiceName(const WCHAR *_pServiceName)
{
    m_ServiceName.Clear();
    return m_ServiceName.append(_pServiceName);
}


const WCHAR *
Share::GetServiceName()
{
    return m_ServiceName.GetString();
}

const CHAR *
Share::GetShareNameSZ()
{
    return pShareNameSZ;
}

const WCHAR *
Share::GetShareName()
{
    return pShareName;
}

const WCHAR *
Share::GetDirectoryName()
{
    return m_DirectoryName.GetString();
}

const CHAR *
Share::GetRemark()
{
    return pRemark;
}

BYTE
Share::GetShareType()
{
    return shareType;
}

HRESULT
Share::SetShareType(BYTE _shareType)
{
    shareType = _shareType;
    return S_OK;
}


HRESULT
Share::AllowUser(const WCHAR *pUserName, DWORD dwAccess)
{
    HRESULT hr = E_ACCESSDENIED;
    CReg RegAllowAll;
    BOOL fAllowAll = FALSE;

    //
    // See if they have full control first
    if(!RequireAuth() || IsAccessAllowed((WCHAR *)pUserName, NULL, (WCHAR *)(m_FullACLList.GetString()), FALSE)) {
        hr = S_OK;
        goto Done;
    }

    //
    // See if they have read only (only if they are trying to get SEC_READ)
    if(SEC_READ == dwAccess && IsAccessAllowed((WCHAR *)pUserName, NULL, (WCHAR *)(m_ReadOnlyACLList.GetString()), FALSE)) {
        hr = S_OK;
        goto Done;
    }

    //
    // See if security is disabled wholesale
    if(TRUE == RegAllowAll.Open(HKEY_LOCAL_MACHINE, L"Services\\SMBServer\\Shares")) {
        fAllowAll = !(RegAllowAll.ValueDW(L"UseAuthentication", TRUE));
        if(TRUE == fAllowAll) {
           hr = S_OK;
           goto Done;
        }
    }
    hr = E_ACCESSDENIED;

    Done:
        return hr;;
}

HRESULT
Share::SetACL(const WCHAR *pFullACL, const WCHAR *pReadACL) {
    HRESULT hr;

    if(FAILED(hr = m_FullACLList.Clear())) {
        goto Done;
    }
    if(FAILED(hr = m_ReadOnlyACLList.Clear())) {
        goto Done;
    }
    if(FAILED(hr = m_FullACLList.append(pFullACL))) {
        goto Done;
    }
    if(FAILED(hr = m_ReadOnlyACLList.append(pReadACL))) {
        goto Done;
    }

    Done:
        if(FAILED(hr)) {
            m_FullACLList.Clear();
            m_ReadOnlyACLList.Clear();
        }

        return hr;
}

HRESULT
Share::IsValidPath(const WCHAR *pPath)
{
    if(0 != wcsstr(pPath, L"\\..\\")) {
        RETAILMSG(1, (L"SMBSRV: Invalid char in path %s\n", pPath));
        return E_FAIL;
    } else if(0 != wcsstr(pPath, L"\\\\")) {
        RETAILMSG(1, (L"SMBSRV: Invalid char in path %s\n", pPath));
        return E_FAIL;
    }
    else {
        return S_OK;
    }
}



/******************************************************************************
 *
 *  ShareManager -- this utility class manages shares on the system.
 *     The class is used to collect functions related to which shares are on the
 *     system
 *
 ******************************************************************************/
ShareManager::ShareManager()
{
#ifdef DEBUG
    //
    // Just used to make sure (in debug) that multiple instances of ShareManager
    //   dont get created
    if(fFirstInst) {
        fFirstInst = FALSE;
    }
    else {
        ASSERT(FALSE);
        TRACEMSG(ZONE_ERROR, (L"Only one instance of ShareManager allowed!!!"));
    }
#endif
    InitializeCriticalSection(&csShareManager);
}

ShareManager::~ShareManager()
{
    //
    // Just used to make sure (in debug) that multiple instances of ShareManager
    //   dont get created
#ifdef DEBUG
    ASSERT(FALSE == fFirstInst);
    fFirstInst = TRUE;
#endif

    //clear our the share lists
    CCritSection csLock(&csShareManager);
    csLock.Lock();

    //
    // Delete all Shares that are outstanding
    while(MasterListOfShares.size()) {
        Share *pToDel = MasterListOfShares.front();
        ASSERT(pToDel);
        MasterListOfShares.pop_front();
        delete pToDel;
    }
    csLock.UnLock();
    DeleteCriticalSection(&csShareManager);
}


HRESULT
ShareManager::AddShare(Share *pShare)
{
    if(NULL == pShare)
        return E_INVALIDARG;

    if(SearchForShare(pShare->GetShareName())) {
        RETAILMSG(1, (L"SMBSRV: ERROR!  adding share %s when it already exists!", pShare->GetShareName()));
        return E_FAIL;
    }
    CCritSection csLock(&csShareManager);
    csLock.Lock();
    if(!MasterListOfShares.push_front(pShare)) {
        return E_OUTOFMEMORY;
    } else {
        return S_OK;
    }
}

HRESULT
ShareManager::DeleteShare(Share *pShare)
{
    if(NULL == pShare)
        return E_INVALIDARG;

    BOOL fFound = FALSE;
    CCritSection csLock(&csShareManager);
    csLock.Lock();
    ce::list<Share *, SHARE_MANANGER_SHARE_ALLOC >::iterator it;


    for(it=MasterListOfShares.begin(); it!=MasterListOfShares.end(); it++) {
        if(pShare == (*it)) {
            MasterListOfShares.erase(it);
            delete pShare;
            fFound = TRUE;
            break;
        }
    }
    return (TRUE == fFound)?S_OK:E_FAIL;
}

HRESULT
ShareManager::ReloadACLS()
{
    Share *pRet = NULL;
    CCritSection csLock(&csShareManager);
    csLock.Lock();

    ce::list<Share *, SHARE_MANANGER_SHARE_ALLOC >::iterator it;
    ce::list<Share *, SHARE_MANANGER_SHARE_ALLOC >::iterator itEnd = MasterListOfShares.end();

    for(it = MasterListOfShares.begin(); it != itEnd; ++it) {
        CReg ShareKey;
        WCHAR wcACL[MAX_PATH];
        WCHAR wcROACL[MAX_PATH];

        StringConverter ShareName;
        ShareName.append(L"Services\\SMBServer\\Shares\\");
        ShareName.append((*it)->GetShareName());

        if(FALSE == ShareKey.Open(HKEY_LOCAL_MACHINE, ShareName.GetString())) {
            continue;
        }
        if(FALSE == ShareKey.ValueSZ(L"UserList", wcACL, MAX_PATH/sizeof(WCHAR))) {
            wcACL[0] = NULL;
        }
        if(FALSE == ShareKey.ValueSZ(L"ROUserList", wcROACL, MAX_PATH/sizeof(WCHAR))) {
            wcROACL[0] = NULL;
        }
        (*it)->SetACL(wcACL, wcROACL);
    }
    return S_OK;
}


Share *
ShareManager::SearchForShare(const WCHAR *pName)
{
    Share *pRet = NULL;
    if(NULL == pName) {
        ASSERT(FALSE);
        return NULL;
    }
    CCritSection csLock(&csShareManager);
    csLock.Lock();

    ce::list<Share *, SHARE_MANANGER_SHARE_ALLOC >::iterator it;
    ce::list<Share *, SHARE_MANANGER_SHARE_ALLOC >::iterator itEnd = MasterListOfShares.end();

    for(it = MasterListOfShares.begin(); it != itEnd; ++it) {
        const WCHAR *pShareName = (*it)->GetShareName();
        ASSERT(pShareName);

        if(pShareName && 0 == _wcsicmp(pShareName, pName)) {
            pRet = *it;
            goto Done;
        }
    }
    Done:
        return pRet;
}


Share *
ShareManager::SearchForShare(UINT uiIdx) {
    Share *pRet = NULL;

    CCritSection csLock(&csShareManager);
    csLock.Lock();

    ce::list<Share *, SHARE_MANANGER_SHARE_ALLOC >::iterator it;
    ce::list<Share *, SHARE_MANANGER_SHARE_ALLOC >::iterator itEnd = MasterListOfShares.end();

    if(uiIdx+1 > MasterListOfShares.size()) {
        pRet = NULL;
        goto Done;
    }

    for(it = MasterListOfShares.begin(); it != itEnd, uiIdx; ++it, uiIdx--) {
    }
    pRet = *it;

    Done:
        return pRet;
}




UINT
ShareManager::NumShares()
{
    UINT uiSize;
    CCritSection csLock(&csShareManager);
    csLock.Lock();
        uiSize = MasterListOfShares.size();
    return uiSize;
}


UINT
ShareManager::NumVisibleShares()
{
    UINT uiSize = 0;
    CCritSection csLock(&csShareManager);
    csLock.Lock();

    ce::list<Share *, SHARE_MANANGER_SHARE_ALLOC >::iterator it;
    ce::list<Share *, SHARE_MANANGER_SHARE_ALLOC >::iterator itEnd = MasterListOfShares.end();

    for(it = MasterListOfShares.begin(); it != itEnd; ++it) {
        if((*it)->IsHidden())
            uiSize ++;
    }
    return uiSize;
}


//
// NOTE: to use this function, you *MUST* Release() the pJobRet
HRESULT
ShareManager::SearchForPrintJob(USHORT usJobID, PrintJob **pJobRet, SMBPrintQueue **pQueueRet)
{
    HRESULT hr = E_FAIL;

    CCritSection csLock(&csShareManager);
    csLock.Lock();

    ce::list<Share *, SHARE_MANANGER_SHARE_ALLOC >::iterator it;
    ce::list<Share *, SHARE_MANANGER_SHARE_ALLOC >::iterator itEnd = MasterListOfShares.end();
    PrintJob *pRet = NULL;

    for(it = MasterListOfShares.begin(); it != itEnd; ++it) {
        SMBPrintQueue *pQueue = (*it)->GetPrintQueue();

        if(pQueue && SUCCEEDED(pQueue->FindPrintJob(usJobID, &pRet))) {
            if(pJobRet)
                *pJobRet = pRet;
            if(pQueueRet)
                *pQueueRet = pQueue;
            hr = S_OK;
            break;
        }
    }
    return hr;
}



TIDState::TIDState():pShare(NULL),
                     usTid(0xFFFF) {
    InitializeCriticalSection(&csTIDLock);
}


#ifdef DEBUG
VOID
TIDState::DebugPrint()
{
    ce::list<ce::smart_ptr<SMBFileStream>, TIDSTATE_FID_STREAM_ALLOC >::iterator it = FIDList.begin();
    ce::list<ce::smart_ptr<SMBFileStream>, TIDSTATE_FID_STREAM_ALLOC >::iterator itEnd = FIDList.end();

    for(it = FIDList.begin(); it != itEnd; it++) {
        TRACEMSG(ZONE_ERROR, (L"    FID(%d) : %s", (*it)->FID(), (*it)->GetFileName()));
    }
}
#endif



HRESULT
TIDState::Init(Share *_pShare)
{
    CCritSection csLock(&csTIDLock);
    csLock.Lock();

    ASSERT(0 == FIDList.size());
    HRESULT hr = E_FAIL;

    //ONLY init ONCE!
    ASSERT(NULL == pShare && 0xFFFF == usTid);

    //
    // Get a fresh TID
    if(FAILED(UIDTidGenerator.GetID(&usTid))) {
        TRACEMSG(ZONE_ERROR, (L"SMB_SRVR: Error -- TIDState couldnt get fresh TID!"));
        ASSERT(FALSE);
        goto Done;
    }
    pShare = _pShare;

    hr = S_OK;
    Done:
        return hr;
}

TIDState::~TIDState()
{
    //
    // Clean up all FIDS as the manager is going down (most likely because
    //   the TID manager is pulling us down... maybe the connction died
    //   or the server is being stopped)
    while(FIDList.size()) {
        RemoveFileStream((FIDList.front())->FID());
    }

    ASSERT(0 == FIDList.size());

    //
    // Free up our TID
    if(0xFFFF != usTid) {
        UIDTidGenerator.RemoveID(usTid);
    }

    DeleteCriticalSection(&csTIDLock);
}

USHORT
TIDState::TID() {
    CCritSection csLock(&csTIDLock);
    csLock.Lock();
    return usTid;
}


HRESULT
TIDState::RemoveFileStream(USHORT usFID)
{
    CCritSection csLock(&csTIDLock);
    csLock.Lock();
    ce::list<ce::smart_ptr<SMBFileStream>, TIDSTATE_FID_STREAM_ALLOC >::iterator it = FIDList.begin();
    ce::list<ce::smart_ptr<SMBFileStream>, TIDSTATE_FID_STREAM_ALLOC >::iterator itEnd = FIDList.end();
    BOOL fFound = FALSE;

    while(it != itEnd) {
        ASSERT(*it);
        if(*it && usFID == (*it)->FID())  {
            FIDList.erase(it++);
            fFound = TRUE;
            break;
        }
        it++;
    }

    if(fFound)  {
        UniqueFID.RemoveID(usFID);
        return S_OK;
    }
    else
        return E_FAIL;
}


HRESULT
TIDState::AddFileStream(SMBFileStream *pConnection)
{
    CCritSection csLock(&csTIDLock);
    csLock.Lock();
    if(!FIDList.push_front(pConnection)) {
        return E_OUTOFMEMORY;
    } else {
        return S_OK;
    }
}

//
// Class factory for building stream -- it builds the stream based on
//   the Share attached to the TIDState
SMBFileStream *
TIDState::CreateFileStream(ce::smart_ptr<ActiveConnection> pMyConnection)
{
    CCritSection csLock(&csTIDLock);
    csLock.Lock();

    SMBFileStream *pRet = NULL;
    Share *pShare = this->GetShare();
    PrintJob *pNewJob = NULL;
    USHORT usFID = 0xFFFF;

    //
    // Make sure this is actually a print queue and fetch queue
    if(g_fPrintServer && STYPE_PRINTQ == pShare->GetShareType()){
        TRACEMSG(ZONE_SMB, (L"SMBSRV-PktHandler: SMB_OpenX -- the TID is for a printer!!"));
    }
    else if(g_fFileServer && STYPE_DISKTREE == pShare->GetShareType()) {
        TRACEMSG(ZONE_SMB, (L"SMBSRV-PktHandler: SMB_OpenX -- the TID is for file server!!"));
    }
    else if(STYPE_IPC == pShare->GetShareType()) {
        TRACEMSG(ZONE_SMB, (L"SMBSRV-PktHandler: SMB_OpenX -- the TID is for IPC!!"));
    }
    else {
        ASSERT(FALSE);
        TRACEMSG(ZONE_ERROR, (L"SMBSRV-PktHandler: SMB_OpenX -- the Tid is for something we dont understand!!"));
        goto Done;
    }

    //
    // Generate a FID
    if(FAILED(UniqueFID.GetID(&usFID))) {
        goto Done;
    }

    //
    // If the TIDState is for a print share, give them back a print stream
    if(STYPE_PRINTQ == pShare->GetShareType()) {

        //
        // Create a new print job
        if(NULL == (pNewJob = new PrintJob(this->GetShare(), usFID))) {
            goto Done;
        }

        //
        // Set our username
        if(FAILED(pNewJob->SetOwnerName(pMyConnection->UserName()))) {
            goto Done;
        }

        //
        // Create a new PrintStream and then add the job to TIDState
        if(!(pRet = new PrintStream(pNewJob,this))) {
           goto Done;
        }
    } else if(STYPE_DISKTREE == pShare->GetShareType()) {
        if(!(pRet = GetNewFileStream(this, usFID))) {
            goto Done;
        }
    }
#ifdef SMB_NMPIPE
    else if(STYPE_IPC == pShare->GetShareType()) {
        if(!(pRet = new IPCStream(this, usFID))) {
            goto Done;
        }
    }
#endif

    Done:
        if(NULL != pNewJob) {
            pNewJob->Release();
        }
        if(!pRet && 0xFFFF != usFID) {
            UniqueFID.RemoveID(usFID);
        }

        return pRet;
}



HRESULT
TIDState::Read(USHORT usFID,
             BYTE *pDest,
             DWORD dwOffset,
             DWORD dwReqSize,
             DWORD *pdwRead)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) cant find FID:0x%x on Read()", this->usTid, usFID));
        goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->Read(pDest, dwOffset, dwReqSize, pdwRead))) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) (FID:0x%x) operation Read() failed", this->usTid, usFID));
        goto Done;
    } else {
        CCritSection csLock(&SMB_Globals::g_Bookeeping_CS);
        csLock.Lock();
        SMB_Globals::g_Bookeeping_TotalRead.QuadPart += *pdwRead;
    }

    //
    // Success
    hr = S_OK;
    Done:
        return hr;
}

 HRESULT
 TIDState::Write(USHORT usFID,
              BYTE *pSrc,
              DWORD dwOffset,
              DWORD dwReqSize,
              DWORD *pdwWritten)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) cant find FID:0x%x on Write()", this->usTid, usFID));
        goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->Write(pSrc, dwOffset, dwReqSize, pdwWritten))) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) (FID:0x%x) operation Write() failed", this->usTid, usFID));
        goto Done;
    } else {
        CCritSection csLock(&SMB_Globals::g_Bookeeping_CS);
        csLock.Lock();
        SMB_Globals::g_Bookeeping_TotalWritten.QuadPart += *pdwWritten;
    }

    //
    // Success
    hr = S_OK;
    Done:
        return hr;
}

HRESULT
TIDState::Open(USHORT usFID,
              const WCHAR *pFileName,
              DWORD dwAccess,
              DWORD dwDisposition,
              DWORD dwAttributes,
              DWORD dwShareMode)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) cant find (FID:0x%x) on Open()", this->usTid, usFID));
        goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->Open(pFileName, dwAccess, dwDisposition, dwAttributes, dwShareMode))) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) (FID:0x%x) operation Open() failed", this->usTid, usFID));
        goto Done;
    }

    //
    // Success
    hr = S_OK;
    Done:
        return hr;
}

HRESULT
TIDState::IsLocked(USHORT usFID,
                  SMB_LARGELOCK_RANGE *pRangeLock,
                  BOOL *pfLocked)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: IsLocked(TID:0x%x) cant find (FID:0x%x) on IsLocked()", this->usTid, usFID));
        goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->IsLocked(pRangeLock, pfLocked))) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: IsLocked(TID:0x%x) (FID:0x%x) operation IsLocked() failed", this->usTid, usFID));
        goto Done;
    }

    //
    // Success
    hr = S_OK;

    Done:
        return hr;
}

HRESULT
TIDState::Lock(USHORT usFID,
               SMB_LARGELOCK_RANGE *pRangeLock)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: Lock(TID:0x%x) cant find (FID:0x%x) on Lock()", this->usTid, usFID));
        goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->Lock(pRangeLock))) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: Lock(TID:0x%x) (FID:0x%x) operation Lock() failed", this->usTid, usFID));
        goto Done;
    }

    //
    // Success
    hr = S_OK;

    Done:
        return hr;
}

HRESULT
TIDState::UnLock(USHORT usFID,
                    SMB_LARGELOCK_RANGE *pRangeLock)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: UnLock(TID:0x%x) cant find (FID:0x%x) on UnLock()", this->usTid, usFID));
        goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->UnLock(pRangeLock))) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: UnLock(TID:0x%x) (FID:0x%x) operation UnLock() failed", this->usTid, usFID));
        goto Done;
    }

    //
    // Success
    hr = S_OK;
    Done:
        return hr;
}


HRESULT
TIDState::BreakOpLock(USHORT usFID)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: BreakOpLock(TID:0x%x) cant find (FID:0x%x) on UnLock()", this->usTid, usFID));
        goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->BreakOpLock())) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: UnLock(TID:0x%x) (FID:0x%x) operation UnLock() failed", this->usTid, usFID));
        goto Done;
    }

    //
    // Success
    hr = S_OK;
    Done:
        return hr;

}


HRESULT
TIDState::SetEndOfStream(USHORT usFID,
                       DWORD dwOffset)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) cant find (FID:0x%x) on SetEndOfStream()", this->usTid, usFID));
        goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->SetEndOfStream(dwOffset))) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) (FID:0x%x) operation SetEndOfStream() failed", this->usTid, usFID));
        goto Done;
    }

    //
    // Success
    hr = S_OK;
    Done:
        return hr;
}

HRESULT
TIDState::Close(USHORT usFID)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) cant find (FID:0x%x) on Close()", this->usTid, usFID));
        hr = E_FAIL;
        goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->Close())) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) (FID:0x%x) operation Close() failed", this->usTid, usFID));
        goto Done;
    }

    //
    // Success
    hr = S_OK;
    Done:
        return hr;
}

HRESULT
TIDState::GetFileSize(USHORT usFID,
                   DWORD *pdwSize)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) cant find (FID:0x%x) on GetFileSize()", this->usTid, usFID));
        goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->GetFileSize(pdwSize))) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) (FID:0x%x) operation GetFileSize() failed", this->usTid, usFID));
        goto Done;
    }

    //
    // Success
    hr = S_OK;
    Done:
        return hr;
}

HRESULT
TIDState::Delete(USHORT usFID)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
       TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) cant find (FID:0x%x) on Delete()", this->usTid, usFID));
       goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->Delete())) {
       TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) (FID:0x%x) operation Delete() failed", this->usTid, usFID));
       goto Done;
    }

    //
    // Success
    hr = S_OK;
    Done:
       return hr;

}


HRESULT
TIDState::SetFileTime(USHORT usFID,
                    FILETIME *pCreation,
                    FILETIME *pAccess,
                    FILETIME *pWrite)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // If they arent setting anything, return okay
    if(!pCreation && !pAccess && !pWrite) {
        hr = S_OK;
        goto Done;
    }

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) cant find (FID:0x%x) on SetFileTime()", this->usTid, usFID));
        goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->SetFileTime(pCreation, pAccess, pWrite))) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) (FID:0x%x) operation SetFileTime() failed", this->usTid, usFID));
        goto Done;
    }

    //
    // Success
    hr = S_OK;
    Done:
        return hr;
}


HRESULT
TIDState::Flush(USHORT usFID)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;
    CCritSection csLock(&csTIDLock);
    csLock.Lock();

    //
    // if the FID is 0xFFFF flush everyone (per smbhlp.zip spec)
    if(0xFFFF == usFID)  {
        ce::list<ce::smart_ptr<SMBFileStream>, TIDSTATE_FID_STREAM_ALLOC >::iterator it;
        ce::list<ce::smart_ptr<SMBFileStream>, TIDSTATE_FID_STREAM_ALLOC >::iterator itEnd = FIDList.end();

        for(it = FIDList.begin(); it != itEnd; ++it) {
            ce::smart_ptr<SMBFileStream> pFound = *it;
            if(FAILED(hr = pFound->Flush())) {
                TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) cant find (FID:0x%x) on batch Flush()", this->usTid, pFound->FID()));
                goto Done;
            }
        }
    } else {

        //
        // Search out the SMBFileStream
        if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
            TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) cant find (FID:0x%x) on Flush()", this->usTid, usFID));
            goto Done;
        }

        //
        // Perform our operation
        if(FAILED(hr = pStream->Flush())) {
            TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) (FID:0x%x) operation Flush() failed", this->usTid, usFID));
            goto Done;
        }
    }

    //
    // Success
    hr = S_OK;
    Done:
        return hr;
}


HRESULT
TIDState::QueryFileInformation(USHORT usFID, WIN32_FIND_DATA *fd, DWORD *pdwNumberOfLinks)
{
    HANDLE hFileHand = INVALID_HANDLE_VALUE; // = FindFirstFile(pObject->FileName(), &fd);
    ce::smart_ptr<SMBFileStream> pStream = NULL;
    HRESULT hr = E_FAIL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) cant find (FID:0x%x) on QueryFileInformation()", this->usTid, usFID));
        goto Done;
    }

    //
    // Make the query
    hr = pStream->QueryFileInformation(fd, pdwNumberOfLinks);

    Done:
        return hr;
}


HRESULT
TIDState::SetFilePointer(USHORT usFID,
                    LONG lDistance,
                    DWORD dwMoveMethod,
                    ULONG *pOffset)
{
    HRESULT hr = E_FAIL;
    ce::smart_ptr<SMBFileStream> pStream = NULL;

    //
    // Search out the SMBFileStream
    if(FAILED(hr = FindFileStream(usFID, pStream)) || !pStream) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) cant find (FID:0x%x) on SetFilePointer()", this->usTid, usFID));
        goto Done;
    }

    //
    // Perform our operation
    if(FAILED(hr = pStream->SetFilePointer(lDistance, dwMoveMethod, pOffset))) {
        TRACEMSG(ZONE_ERROR, (L"SMB-SRV: TIDState(TID:0x%x) (FID:0x%x) operation SetFilePointer() failed", this->usTid, usFID));
        goto Done;
    }

    //
    // Success
    hr = S_OK;
    Done:
        return hr;
}


HRESULT
TIDState::FindFileStream(USHORT usFID, ce::smart_ptr<SMBFileStream> &pStream)
{
    HRESULT hr = E_FAIL;
    CCritSection csLock(&csTIDLock);
    csLock.Lock();

    pStream = NULL;
    ce::list<ce::smart_ptr<SMBFileStream>, TIDSTATE_FID_STREAM_ALLOC >::iterator it;
    ce::list<ce::smart_ptr<SMBFileStream>, TIDSTATE_FID_STREAM_ALLOC >::iterator itEnd = FIDList.end();

    for(it = FIDList.begin(); it != itEnd; ++it) {
        ce::smart_ptr<SMBFileStream> pFound = *it;
        ASSERT(pFound);
        if(pFound && usFID == pFound->FID()) {
            pStream = pFound;
            hr = S_OK;
            break;
        }
    }
    return hr;
}

