//+--------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1992.
//
//  File:       storage.cxx
//
//  Contents:   Contains generic storage APIs
//
//  History:    05-Oct-92       DrewB   Created
//
//---------------------------------------------------------------

#include <exphead.cxx>
#pragma hdrstop

#include <dfentry.hxx>
#include <storagep.h>
#include <logfile.hxx>
#include <df32.hxx>
#ifdef COORD
#include <oledb.h>
#endif //COORD

#include <trace.hxx>

#include <ole2sp.h>
#include <ole2com.h>


//+--------------------------------------------------------------
//
//  Function:   StgOpenStorage, public
//
//  Synopsis:   Instantiates a root storage from a file
//              by binding to the appropriate implementation
//              and starting things up
//
//  Arguments:  [pwcsName] - Name
//              [pstgPriority] - Priority mode reopen IStorage
//              [grfMode] - Permissions
//              [snbExclude] - Exclusions for priority reopen
//              [reserved]
//              [ppstgOpen] - Docfile return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppstgOpen]
//
//  History:    05-Oct-92       DrewB   Created
//
//---------------------------------------------------------------

STDAPI StgOpenStorage(OLECHAR const *pwcsName,
                      IStorage *pstgPriority,
                      DWORD grfMode,
                      SNB snbExclude,
                      LPSTGSECURITY reserved,
                      IStorage **ppstgOpen)
{
    return DfOpenDocfile(pwcsName, NULL, pstgPriority, grfMode,
                         snbExclude, reserved, NULL, 0, ppstgOpen);
}

//+---------------------------------------------------------------------------
//
//  Function:   CheckSignature, private
//
//  Synopsis:   Checks the given memory against known signatures
//
//  Arguments:  [pb] - Pointer to memory to check
//
//  Returns:    S_OK - Current signature
//              S_FALSE - Beta 2 signature, but still successful
//              Appropriate status code
//
//  History:    23-Jul-93       DrewB   Created from header.cxx code
//
//----------------------------------------------------------------------------

//Identifier for first bytes of Beta 1 Docfiles
const BYTE SIGSTG_B1[] = {0xd0, 0xcf, 0x11, 0xe0, 0x0e, 0x11, 0xfc, 0x0d};
const USHORT CBSIGSTG_B1 = sizeof(SIGSTG_B1);

//Identifier for first bytes of Beta 2 Docfiles
const BYTE SIGSTG_B2[] = {0x0e, 0x11, 0xfc, 0x0d, 0xd0, 0xcf, 0x11, 0xe0};
const BYTE CBSIGSTG_B2 = sizeof(SIGSTG_B2);

SCODE CheckSignature(BYTE *pb)
{
    SCODE sc;

    olDebugOut((DEB_ITRACE, "In  CheckSignature(%p)\n", pb));

    // Check for ship Docfile signature first
    if (memcmp(pb, SIGSTG, CBSIGSTG) == 0)
        sc = S_OK;

    // Check for Beta 2 Docfile signature
    else if (memcmp(pb, SIGSTG_B2, CBSIGSTG_B2) == 0)
        sc = S_FALSE;

    // Check for Beta 1 Docfile signature
    else if (memcmp(pb, SIGSTG_B1, CBSIGSTG_B1) == 0)
        sc = STG_E_OLDFORMAT;
    else
        sc = STG_E_INVALIDHEADER;

    olDebugOut((DEB_ITRACE, "Out CheckSignature => %lX\n", sc));
    return sc;
}

//+--------------------------------------------------------------
//
//  Function:   StgIsStorageFileHandle, private
//
//  Synopsis:   Determines whether a handle is open on a storage file.
//              Spun off from StgIsStorageFile.  Internaly we use this
//
//  Arguments:  [hf] - Open File Handle (caller must seek it to 0)
//
//  Returns:    S_OK, S_FALSE or error codes
//
//  History:    07-May-98   MikeHill   Created
//              05-June-98  BChapman   Return Errors not just S_FALSE.
//                                     Add optional Overlapped pointer.
//
//---------------------------------------------------------------


STDAPI StgIsStorageFileHandle( HANDLE hf, LPOVERLAPPED povlp )
{
    DWORD cbRead;
    BYTE stgHeader[sizeof(SStorageFile)];
    SCODE sc;
    LONG status;
    OVERLAPPED ovlp;

    FillMemory( stgHeader, sizeof(SStorageFile), 0xDE );

    if (povlp == NULL)
    {
    ovlp.Offset = 0;
    ovlp.OffsetHigh = 0;
    ovlp.hEvent = NULL;
    }

    if( !ReadFile( hf,
           &stgHeader,
           sizeof( stgHeader ),
           &cbRead,
           (povlp == NULL) ? &ovlp : povlp ) )
    {
        if( NULL != povlp )
        {
            status = GetLastError();
            if( ERROR_IO_PENDING == status)
            {
                status = ERROR_SUCCESS;
                if( !GetOverlappedResult( hf, povlp, &cbRead, TRUE ) )
                    status = GetLastError();
            }
            if(ERROR_SUCCESS != status && ERROR_HANDLE_EOF != status)
                olChk( HRESULT_FROM_WIN32( status ) );
        }
        else
            olErr( EH_Err, S_FALSE );
    }

    // Don't worry about short reads.  If the read is short then
    // the signature checks will fail.

    sc = CheckSignature( ((SStorageFile*)stgHeader)->abSig );
    if(S_OK == sc)
        goto EH_Err;    // Done, return "Yes"

    olChk(sc);

    // It didn't error.  sc != S_OK then it
    // Must be S_FALSE.
    olAssert(S_FALSE == sc);

EH_Err:
    if( (STG_E_OLDFORMAT == sc) || (STG_E_INVALIDHEADER == sc) )
        sc = S_FALSE;

    return sc;
}


//+--------------------------------------------------------------
//
//  Function:   StgIsStorageFile, public
//
//  Synopsis:   Determines whether the named file is a storage or not
//
//  Arguments:  [pwcsName] - Filename
//
//  Returns:    S_OK, S_FALSE or error codes
//
//  History:    05-Oct-92       DrewB   Created
//
//---------------------------------------------------------------


STDAPI StgIsStorageFile(OLECHAR const *pwcsName)
{
    HANDLE hf;
    SCODE sc;

    olLog(("--------::In  StgIsStorageFile(" OLEFMT ")\n", pwcsName));

    TRY
    {
#ifndef OLEWIDECHAR
        if (FAILED(sc = ValidateNameA(pwcsName, _MAX_PATH)))
#else
        if (FAILED(sc = ValidateNameW(pwcsName, _MAX_PATH)))
#endif
            return ResultFromScode(sc);

#if !defined(UNICODE)

    // Chicago - call ANSI CreateFile

    char szName[_MAX_PATH + 1];

    if (!WideCharToMultiByte(
            AreFileApisANSI() ? CP_ACP : CP_OEMCP,
            0,
            pwcsName,
            -1,
            szName,
            _MAX_PATH + 1,
            NULL,
            NULL))
        return ResultFromScode(STG_E_INVALIDNAME);

    hf = CreateFileA (
            szName,
            GENERIC_READ,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL
            );

#else

        hf = CreateFile (
            pwcsName,
            GENERIC_READ,
            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL
            );
#endif // !defined(UNICODE)

        if (hf == INVALID_HANDLE_VALUE)
            return ResultFromScode(STG_SCODE(GetLastError()));

        sc = StgIsStorageFileHandle( hf, NULL );
        CloseHandle (hf);

    }
    CATCH(CException, e)
    {
        sc = e.GetErrorCode();
    }
    END_CATCH

    olLog(("--------::Out StgIsStorageFile().  ret == %lx\n", sc));

    return(ResultFromScode(sc));
}

//+--------------------------------------------------------------
//
//  Function:   StgIsStorageILockBytes, public
//
//  Synopsis:   Determines whether the ILockBytes is a storage or not
//
//  Arguments:  [plkbyt] - The ILockBytes
//
//  Returns:    S_OK, S_FALSE or error codes
//
//  History:    05-Oct-92       DrewB   Created
//
//---------------------------------------------------------------

STDAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
{
    OLETRACEIN((API_StgIsStorageILockBytes, PARAMFMT("plkbyt= %p"), plkbyt));

    HRESULT hr;
    SCODE sc;
    SStorageFile stgfile;
    ULONG cbRead;
    ULARGE_INTEGER ulOffset;

    TRY
    {
        if (FAILED(sc = ValidateInterface(plkbyt, IID_ILockBytes)))
        {
            hr = ResultFromScode(sc);
            goto errRtn;
        }
        ULISet32(ulOffset, 0);
        hr = plkbyt->ReadAt(ulOffset, &stgfile, sizeof(stgfile), &cbRead);
        if (FAILED(GetScode(hr)))
        {
            goto errRtn;
        }
    }
    CATCH(CException, e)
    {
        hr = ResultFromScode(e.GetErrorCode());
        goto errRtn;
    }
    END_CATCH

    if (cbRead == sizeof(stgfile))
    {
        if ((memcmp((void *)stgfile.abSig, SIGSTG, CBSIGSTG) == 0) ||
            (memcmp((void *)stgfile.abSig, SIGSTG_B2, CBSIGSTG_B2) == 0))
        {
            hr = ResultFromScode(S_OK);
            goto errRtn;
        }
    }

    hr = ResultFromScode(S_FALSE);

errRtn:
    OLETRACEOUT((API_StgIsStorageILockBytes, hr));

    return hr;
}

//+--------------------------------------------------------------
//
//  Function:   StgSetTimes
//
//  Synopsis:   Sets file time stamps
//
//  Arguments:  [lpszName] - Name
//              [pctime] - create time
//              [patime] - access time
//              [pmtime] - modify time
//
//  Returns:    Appropriate status code
//
//  History:    05-Oct-92       AlexT   Created
//
//---------------------------------------------------------------

STDAPI StgSetTimes(OLECHAR const *lpszName,
                   FILETIME const *pctime,
                   FILETIME const *patime,
                   FILETIME const *pmtime)
{
    SCODE sc;
    HANDLE hFile;

    TRY
    {
#ifndef OLEWIDECHAR
        sc = ValidateNameA(lpszName, _MAX_PATH);
#else
        sc = ValidateNameW(lpszName, _MAX_PATH);
#endif
        if (SUCCEEDED(sc))
        {
#if !defined(UNICODE) && defined(OLEWIDECHAR)
            //Chicago - call ANSI CreateFile
            char szName[_MAX_PATH];

            if (!WideCharToMultiByte(
                    AreFileApisANSI() ? CP_ACP : CP_OEMCP,
                    0,
                    lpszName,
                    -1,
                    szName,
                    _MAX_PATH,
                    NULL,
                    NULL))
                return ResultFromScode(STG_E_INVALIDNAME);
            hFile = CreateFileA(szName, GENERIC_WRITE,
                               FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                               NULL);
#else
            hFile = CreateFile(lpszName, GENERIC_WRITE,
                               FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                               OPEN_EXISTING,
                               FILE_ATTRIBUTE_NORMAL,
                               NULL);
#endif
            if (hFile == INVALID_HANDLE_VALUE)
                sc = LAST_STG_SCODE;
            else
            {
                if (!SetFileTime(hFile, (FILETIME *)pctime, (FILETIME *)patime,
                                 (FILETIME *)pmtime))
                    sc = LAST_STG_SCODE;
                CloseHandle(hFile);
            }
        }
    }
    CATCH(CException, e)
    {
        sc = e.GetErrorCode();
    }
    END_CATCH
    return ResultFromScode(sc);
}


#if DBG==1
extern "C" unsigned long filestInfoLevel; //  File I/O layer of Docfile
extern "C" unsigned long nffInfoLevel;    //  NTFS Flat File

void
StgDebugInit()
{
    GetInfoLevel("filest", &filestInfoLevel, "0x0003");
    GetInfoLevel(   "nff", &nffInfoLevel,    "0x0003");
}

#endif // DBG==1


//+---------------------------------------------------------------------------
//
//  Function:   NormalDllGetClassObject, public
//
//  Synopsis:   Registers any normal Inproc Dll server.
//
//  Arguments:  [refCLSID]           Class ID of object to register
//              [pwszDescription]    Description to put in the CLSID Key
//              [pwszDllName]        Dll where the object's code lives
//              [pwszThreadingModel] Threading Model (i.e. L"both")
//
//  Returns:    Appropriate status code
//
//  History:    15-jan-98       BChapman   Created
//
//----------------------------------------------------------------------------

#define STRBUFSZ    64

STDAPI NormalDllRegisterServer(
        REFCLSID    refCLSID,
        WCHAR *     pwszDescription,
        WCHAR *     pwszDllName,
        WCHAR *     pwszThreadingModel)
{

    HKEY hkeyCLSID=NULL;
    HKEY hkeyInproc32=NULL;
    DWORD dwDisp=0;
    LONG rc;
    HRESULT sc=S_OK;

    WCHAR wszCLSID[STRBUFSZ];
    WCHAR wszCLSKey[STRBUFSZ];

//--------------------------------------
// Construct and Open the Class ID key.
//
    if(0 == StringFromGUID2(refCLSID, wszCLSID, STRBUFSZ))
        olErr( EH_Err, E_UNEXPECTED );

    wcscpy(wszCLSKey, L"CLSID\\");
    wcscat(wszCLSKey, wszCLSID);

    rc = RegCreateKeyEx(HKEY_CLASSES_ROOT, wszCLSKey,
                        0, _T(""), REG_OPTION_NON_VOLATILE,
                        KEY_WRITE, NULL,
                        &hkeyCLSID, &dwDisp);
    if(ERROR_SUCCESS != rc)
        olErr( EH_Err, REGDB_E_WRITEREGDB );

//-----------------------------------------------------
// Set the description string on the Class ID Key
//
    rc = RegSetValueEx(hkeyCLSID, _T(""), 0,
                       REG_SZ, (BYTE*) pwszDescription,
                       sizeof(WCHAR)*(1+wcslen(pwszDescription)) );
    if(ERROR_SUCCESS != rc)
        olErr( EH_Err, REGDB_E_WRITEREGDB );

//---------------------------
// Open the Inproc32 Sub-key.
//
    rc = RegCreateKeyEx(hkeyCLSID, _T("InprocServer32"),
                      0, _T(""), REG_OPTION_NON_VOLATILE,
                      KEY_WRITE, NULL,
                      &hkeyInproc32, &dwDisp);
    if(ERROR_SUCCESS != rc)
        olErr( EH_Err, REGDB_E_WRITEREGDB );

//-----------------------------------------
// Set the DLL name on the Inproc32 Sub-Key
//
    rc = RegSetValueEx(hkeyInproc32, _T(""), 0,
                       REG_SZ, (BYTE*) pwszDllName,
                       sizeof(WCHAR)*(1+wcslen(pwszDllName)));
    if(ERROR_SUCCESS != rc)
        olErr( EH_Err, REGDB_E_WRITEREGDB );

//------------------------
// Set the Threading Model
//
    rc = RegSetValueEx(hkeyInproc32, _T("ThreadingModel"), 0,
                       REG_SZ, (BYTE*) pwszThreadingModel,
                       sizeof(WCHAR)*(1+wcslen(pwszThreadingModel)));
    if(ERROR_SUCCESS != rc)
        olErr( EH_Err, REGDB_E_WRITEREGDB );

EH_Err:
    if(NULL != hkeyCLSID)
        RegCloseKey(hkeyCLSID);
    if(NULL != hkeyInproc32)
        RegCloseKey(hkeyInproc32);

    return sc;
}


//+---------------------------------------------------------------------------
//
//  Function:   AddCLSIDToMultiSz, public
//
//  Synopsis:   Add the string form of a Class ID to a MultiSZ registry value.
//
//  Arguments:  [refCLSID]      Class ID to register
//              [hkeyStart]     An open registry key.
//              [wcszKey]       Path to the key reletive to hkeyStart.
//              [wcszValue]     Name of the Value under wcszKey.
//
//  Returns:    Appropriate status code
//
//  History:    18-jan-98       BChapman   Created
//
//----------------------------------------------------------------------------

STDAPI AddCLSIDToMultiSz(
        REFCLSID refCLSID,
        HKEY hkeyStart,
        WCHAR* wcszKey,
        WCHAR* wcszValue)
{
    WCHAR wszCLSID[STRBUFSZ];
    int ccCLSIDstr;
    LONG rc;
    HRESULT sc=S_OK;
    HKEY hkeyKey=NULL;

    BYTE* p_bytes=NULL;
    WCHAR* p_wsz=NULL;
    DWORD cbData, cbDataNew;
    DWORD dwType;

    ccCLSIDstr = StringFromGUID2( refCLSID, wszCLSID, STRBUFSZ );
    if( 0 == ccCLSIDstr )
        olErr( EH_Err, E_UNEXPECTED );
    ccCLSIDstr += 1;   // +1 for the NULL

    // Open the Key
    //
    rc = RegOpenKeyEx(hkeyStart,
                      wcszKey,
                      0,
                      KEY_QUERY_VALUE | KEY_SET_VALUE,
                      &hkeyKey);
    if( ERROR_SUCCESS != rc )
        olErr( EH_Err, REGDB_E_KEYMISSING );

    // Size The Buffer,
    // Allocate the buffer,
    // And Read the data.
    //
    cbData = 0;
    cbDataNew = 0;
    rc = RegQueryValueEx( hkeyKey,
                          wcszValue,
                          0,
                          &dwType,
                          NULL,
                          &cbData );
    if( ERROR_SUCCESS != rc )
    {
        //
        // The value may not exist.  In that case we make a new entry.
        // Rather than depend on an exact error code, lets take the "new"
        // path on all failures.  We will hit real problems in the "write"
        // phase
        cbData = 1*sizeof(WCHAR); // +1 for the terminating second Null.
        cbDataNew = (ccCLSIDstr + 1) * sizeof(WCHAR);
        olMem( p_bytes = new BYTE[ cbDataNew ] );
        memset( p_bytes, 0, cbDataNew );
    }
    else
    {
        if( REG_MULTI_SZ != dwType )
            olErr( EH_Err, REGDB_E_INVALIDVALUE );

        // cbData is in units of bytes
        //
        cbDataNew = cbData + ( ccCLSIDstr * sizeof(WCHAR) );
        olMem( p_bytes = new BYTE[ cbDataNew ] );
        memset( p_bytes, 0, cbDataNew );
        rc = RegQueryValueEx( hkeyKey,
                              wcszValue,
                              0,
                              &dwType,
                              p_bytes,
                              &cbData );
        if( ERROR_SUCCESS != rc )
            olErr( EH_Err, LAST_SCODE );
    }

    // Check if the Class Id is already present
    //
    p_wsz = (WCHAR*)p_bytes;
    while( L'\0' != *p_wsz)
    {
        if( 0 == wcscmp( p_wsz, wszCLSID ) )
            break;

        // Advance to the end of the String.
        while( L'\0' != *p_wsz)
            ++p_wsz;

        // Advance to the start of the next String.
        // Or the terminating second null.
        ++p_wsz;
    }


    // If the Class Id is not already present,
    // Add the Class Id to the list
    // And write it into the Registry
    //
    if( L'\0' == *p_wsz )
    {
        wcscpy( p_wsz, wszCLSID );
        p_wsz[ ccCLSIDstr ] = L'\0';      // Add the Second Null.

        rc = RegSetValueEx( hkeyKey,
                           wcszValue,
                           0,
                           REG_MULTI_SZ,
                           p_bytes,
                           cbDataNew );
        if( ERROR_SUCCESS != rc )
            olErr( EH_Err, REGDB_E_WRITEREGDB );
    }

EH_Err:
    if( NULL != hkeyKey )
        RegCloseKey( hkeyKey );

    if( NULL != p_bytes )
        delete[] p_bytes;

    return sc;
}



//+---------------------------------------------------------------------------
//
//  Function:   DllRegisterServer, public
//
//  Returns:    Appropriate status code
//
//  History:    15-jan-98       BChpaman   Created
//
//----------------------------------------------------------------------------

STDAPI Storage32DllRegisterServer(void)
{
    HRESULT hrAccum=S_OK;

    return hrAccum;
}