//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1993.
//
//  File:   api.cxx
//
//  Contents:   API entry points
//
//  History:    30-Jun-93   DrewB   Created
//
//----------------------------------------------------------------------------


#include "exphead.cxx"
#pragma hdrstop

#include <propapi.h>
#include <hntfsstg.hxx>
#include <dfentry.hxx>
#include <winefs.h>       // for DuplicateEncryptionInfoFile

SCODE ValidateGrfMode( DWORD grfMode, BOOL fCreateAPI );
HRESULT GetNFFTempName (const WCHAR *pwcsFileName, WCHAR *awcsTmpName);
SCODE ValidateStgOptions (STGOPTIONS * pStgOptions, DWORD stgfmt, BOOL fCreate);
inline SCODE ValidateGrfAttrs (DWORD grfAttrs, DWORD stgfmt)
{
    if (stgfmt != STGFMT_DOCFILE)
    {
        if (grfAttrs != 0)
            return STG_E_INVALIDFLAG;
    }
    else
    {
        if ((grfAttrs & ~FILE_FLAG_NO_BUFFERING) != 0)
            return STG_E_INVALIDFLAG;
    }
    return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Function:   DfOpenStorageEx
//
//  Synopsis:   Open storage and stream objects
//
//  Arguments:  [pwcsUsersName] - pathanme of the file
//              [fCreateAPI] - create or open
//              [grfMode] - open mode flags
//              [grfAttrs] -  reserved
//              [stgfmt] -  storage format
//              [pSecurity] - reserved
//              [pTransaction] - reserved
//              [riid] - GUID of interface pointer to return
//              [ppObjectOpen] - interface pointer to return
//  Returns:    Appropriate status code
//
//  History:    12-Jul-95   HenryLee    Created
//
//----------------------------------------------------------------------------

STDAPI DfOpenStorageEx (
            const WCHAR* pwcsUsersName,
            BOOL     fCreateAPI,         // create vs open
            DWORD    grfMode,
            DWORD    stgfmt,             // enum
            DWORD    grfAttrs,           // reserved
            STGOPTIONS *pStgOptions,
            void *   reserved,
            REFIID   riid,
            void **  ppObjectOpen)
{
    HRESULT sc = S_OK;
    DWORD dwFullPathLen;
    WCHAR awcsFullName[_MAX_PATH], *pwcsFile;

    //
    // The ANY and STORAGE formats recursivly call back through here
    // for the correct real format (DOCFILE or FILE).  We only call
    // GetFullPathName on real formats, to avoid redundant calls as we
    // recurse.
    //  This then *requires* that the ANY and STORAGE must recurse (i.e. can't
    // call NFFOpen or docfile directly) because the filename has not been
    // properly prepared.
    //
    // For STGFMT_DOCFILE, let the docfile layer handle name checking
    //
    if(STGFMT_ANY != stgfmt &&
       STGFMT_STORAGE != stgfmt &&
       STGFMT_DOCFILE != stgfmt)
    {
        dwFullPathLen = GetFullPathNameW(pwcsUsersName, _MAX_PATH,
                                         awcsFullName,&pwcsFile);

        if (dwFullPathLen == 0)
        {
            DWORD dwErr = GetLastError();

            // In some circumstances (name == " ", for instance),
            // GetFullPathNameW can return 0 and GetLastError returns 0.
            // We want to return STG_E_INVALIDNAME for these.
            if (dwErr != NOERROR)
            {
                olErr(EH_Err, Win32ErrorToScode(dwErr));
            }
            else
            {
                olErr(EH_Err, STG_E_INVALIDNAME);
            }
        }
        else if (dwFullPathLen > _MAX_PATH)
            olErr(EH_Err, STG_E_PATHNOTFOUND);
    }

    //-----------------------------------------
    //  Switch on STGFMT_
    //      STORAGE, DOCFILE, FILE, ANY
    //
    switch(stgfmt)
    {
    case STGFMT_FILE:
      {
        olChk( NFFOpen( awcsFullName, grfMode, NFFOPEN_NORMAL,
                          fCreateAPI, riid, ppObjectOpen) );

      } // case STGFMT_FILE
    break;

    case STGFMT_ANY:
      {
        DWORD stgfmt2=STGFMT_STORAGE;
        //
        // Attempting to CREATE a Storage with STGFMT_ANY is ambiguous,
        // On NTFS STGFMT_FILE could be appropriate,
        // and is therefore invalid.
        //
        if (fCreateAPI)
            olChk (STG_E_INVALIDPARAMETER);

        //
        //   If IsNffAppropriate() returns S_OK use STGFMT_FILE
        // If it returns STG_E_INVALIDFUNCTION try storage (FAT-FS or Docfile).
        // Any other Error, bubble back to the user
        //
        sc = CNtfsStorage::IsNffAppropriate( pwcsUsersName );
        if( SUCCEEDED( sc ) )
        {
            stgfmt2 = STGFMT_FILE;
        }
        else
        {
            if( STG_E_INVALIDFUNCTION == sc )
                stgfmt2 = STGFMT_STORAGE;
            else
                olChk( sc );
        }

        sc = DfOpenStorageEx (pwcsUsersName, fCreateAPI, grfMode, stgfmt2,
                                grfAttrs, pStgOptions, reserved,
                                riid, ppObjectOpen);

        olChk(sc);

      } // case STGFMT_ANY;
    break;

    case STGFMT_STORAGE:
    case STGFMT_DOCFILE:  // GetFullPathName has not yet been called.
      {
        IStorage *pstg = NULL;
        ULONG ulSectorSize = 0;

        if( fCreateAPI )
        {
            if (grfAttrs & FILE_ATTRIBUTE_TEMPORARY)  // create temp file
                pwcsUsersName = NULL;
            olChk( DfCreateDocfile (pwcsUsersName,
                NULL, grfMode, NULL,
                pStgOptions ? pStgOptions->ulSectorSize : 512,
                grfAttrs, &pstg));
        }
        else
            olChk( DfOpenDocfile (pwcsUsersName,
                    NULL,
                    NULL,
                    grfMode,
                    NULL,
                    0,
                    &ulSectorSize,
                    grfAttrs,
                    &pstg));

        if( IID_IStorage != riid )
        {
            sc = pstg->QueryInterface( riid, ppObjectOpen );
            pstg->Release();

            if (fCreateAPI && !SUCCEEDED(sc) && pwcsUsersName != NULL)
            {
                DeleteFileW (pwcsUsersName); //delete newly create file
            }
        }
        else
        {
            *ppObjectOpen = pstg;
            if (pStgOptions != NULL && !fCreateAPI)
                pStgOptions->ulSectorSize = ulSectorSize;
        }

        olChk(sc);

      }   // case STGFMT_DOCFILE
    break;

    default:
        olErr (EH_Err, STG_E_INVALIDPARAMETER);
        break;
    }

EH_Err:
    return sc;

};

//+---------------------------------------------------------------------------
//
//  Function:   StgCreateStorageEx, public
//
//  Synopsis:   Creates a storage or stream object
//
//  Arguments:  [pwcsName] - pathname of file
//              [grfMode] - open mode flags
//              [stgfmt] -  storage format
//              [grfAttrs] -  reserved
//              [pSecurity] - reserved
//              [pTransaction] - reserved
//              [riid] - GUID of interface pointer to return
//              [ppObjectOpen] - interface pointer to return
//
//  Returns:    Appropriate status code
//
//  History:    12-Jul-95   HenryLee   Created
//
//----------------------------------------------------------------------------

STDAPI StgCreateStorageEx (const WCHAR* pwcsName,
            DWORD grfMode,
            DWORD stgfmt,               // enum
            DWORD grfAttrs,             // reserved
            STGOPTIONS * pStgOptions,
            void * reserved,
            REFIID riid,
            void ** ppObjectOpen)
{
    HRESULT sc = S_OK;
    WCHAR awcsTmpPath[_MAX_PATH];

    olDebugOut((DEB_TRACE, "In  StgCreateStorageEx(%ws, %p, %p, %p, %p)\n",
                pwcsName, grfMode, stgfmt, riid, ppObjectOpen));

    olChk(ValidatePtrBuffer(ppObjectOpen));
    *ppObjectOpen = NULL;

    if (reserved != NULL)
        olErr (EH_Err, STG_E_INVALIDPARAMETER);

    olChk( ValidateGrfAttrs (grfAttrs, stgfmt));
    olChk( ValidateGrfMode( grfMode, TRUE ) );
    olChk( VerifyPerms( grfMode, TRUE ) );

    if (pStgOptions != NULL)
        olChk( ValidateStgOptions(pStgOptions, stgfmt, TRUE));

    if (stgfmt == STGFMT_FILE)
    {
      if (pwcsName != NULL)
      {
        olChk (ValidateNameW (pwcsName, _MAX_PATH));
      }
      else
      {
            olChk (GetNFFTempName (pwcsName, awcsTmpPath));
            pwcsName = awcsTmpPath;

            //Add the STGM_CREATE flag so we don't fail with
            //STG_E_FILEALREADYEXISTS when we see that the file already exists
            //later.
            grfMode |= STGM_CREATE;
            grfAttrs |= FILE_ATTRIBUTE_TEMPORARY;
      }
    }

    if (stgfmt == STGFMT_DOCFILE && 
        pStgOptions != NULL &&
        pStgOptions->usVersion >= 2 &&
        pStgOptions->pwcsTemplateFile != NULL)
    {
        DWORD dwAttrs = GetFileAttributes (pStgOptions->pwcsTemplateFile);

        if (dwAttrs == 0xFFFFFFFF)
            olChk (WIN32_SCODE (GetLastError()));

        if (dwAttrs & FILE_ATTRIBUTE_ENCRYPTED)
        {
            DWORD dwErr = DuplicateEncryptionInfoFile(
                pStgOptions->pwcsTemplateFile,
                pwcsName,
                grfMode & STGM_CREATE ? CREATE_ALWAYS : CREATE_NEW,
                FILE_ATTRIBUTE_NORMAL,
                NULL);
            if (dwErr != ERROR_SUCCESS)
                olChk (WIN32_SCODE(dwErr));

            grfAttrs |= FILE_ATTRIBUTE_ENCRYPTED;
        }
    }

    olChk (DfOpenStorageEx (pwcsName, TRUE, grfMode, stgfmt, grfAttrs,
             pStgOptions, reserved, riid, ppObjectOpen));

    olDebugOut((DEB_TRACE, "Out StgCreateStorageEx => %p\n", *ppObjectOpen));
EH_Err:
    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   StgOpenStorageEx
//
//  Synopsis:   Open storage and stream objects
//
//  Arguments:  [pwcsName] - pathanme of the file
//              [grfMode] - open mode flags
//              [grfAttrs] -  reserved
//              [stgfmt] -  storage format
//              [pSecurity] - reserved
//              [pTransaction] - reserved
//              [riid] - GUID of interface pointer to return
//              [ppObjectOpen] - interface pointer to return
//  Returns:    Appropriate status code
//
//  History:    12-Jul-95   HenryLee    Created
//
//----------------------------------------------------------------------------

STDAPI StgOpenStorageEx (const WCHAR *pwcsName,
            DWORD grfMode,
            DWORD stgfmt,               // enum
            DWORD grfAttrs,             // reserved
            STGOPTIONS * pStgOptions,
            void * reserved,
            REFIID riid,
            void ** ppObjectOpen)
{
    HRESULT sc = S_OK;

    olDebugOut((DEB_TRACE, "In  StgOpenStorageEx(%ws, %p, %p, %p, %p)\n",
                pwcsName, grfMode, stgfmt, riid, ppObjectOpen));

    olChk(ValidatePtrBuffer(ppObjectOpen));
    *ppObjectOpen = NULL;

    if (reserved != NULL)
        olErr (EH_Err, STG_E_INVALIDPARAMETER);

    olChk (ValidateNameW (pwcsName, _MAX_PATH));

    olChk( ValidateGrfAttrs (grfAttrs, stgfmt));
    olChk( ValidateGrfMode( grfMode, FALSE ) );
    olChk( VerifyPerms( grfMode, TRUE ) );

    if (pStgOptions != NULL)
        olChk( ValidateStgOptions(pStgOptions, stgfmt, FALSE));

    olChk (DfOpenStorageEx (pwcsName, FALSE, grfMode, stgfmt, grfAttrs,
             pStgOptions, reserved, riid, ppObjectOpen));


    olDebugOut((DEB_TRACE, "Out StgOpenStorageEx => %p\n", *ppObjectOpen));
EH_Err:

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   ValidateGrfMode
//
//  Synopsis:   Sanity checking for the grfMode. (all implementations)
//
//  Arguments:  [grfMode] -- grfMode to check
//              [fCreateAPI] -- Called from CreateStorage vs. OpenStorage.
//
//  Returns:    Appropriate status code
//
//  History:    30-Mar-98   BChapman    Created
//
//----------------------------------------------------------------------------


SCODE ValidateGrfMode( DWORD grfMode, BOOL fCreateAPI )
{
    HRESULT sc=S_OK;

    // If there are any invalid bits set (error)
    if( 0 != ( grfMode & ~( STGM_DIRECT           |   //         0
                            STGM_TRANSACTED       |   //    1 0000
                            STGM_SIMPLE           |   //  800 0000
                            STGM_READ             |   //         0
                            STGM_WRITE            |   //         1
                            STGM_READWRITE        |   //         2
                            STGM_SHARE_DENY_NONE  |   //        40
                            STGM_SHARE_DENY_READ  |   //        30
                            STGM_SHARE_DENY_WRITE |   //        20
                            STGM_SHARE_EXCLUSIVE  |   //        10
                            STGM_PRIORITY         |   //    4 0000
                            STGM_DELETEONRELEASE  |   //  400 0000
                            STGM_NOSCRATCH        |   //   10 0000
                            STGM_CREATE           |   //      1000
                            STGM_CONVERT          |   //    2 0000
                            STGM_FAILIFTHERE      |   //         0
                            STGM_DIRECT_SWMR      |
                            STGM_NOSNAPSHOT  ) ) )    //   20 0000
    {
        olErr( EH_Err, STG_E_INVALIDFLAG );
    }

    // If you Create for ReadOnly (error)
    if( fCreateAPI && ( ( grfMode & STGM_RDWR ) == STGM_READ ) )
        olErr( EH_Err, STG_E_INVALIDFLAG );

    // if you Open/Create for Convert And DeleteOnRelease (error)
    if( ( grfMode & ( STGM_DELETEONRELEASE | STGM_CONVERT ) )
                    == ( STGM_DELETEONRELEASE | STGM_CONVERT ) )
    {
        olErr(EH_Err, STG_E_INVALIDFLAG);
    }

    if( grfMode & STGM_SIMPLE )
    {
        if( fCreateAPI )
        {
            // If you Create Simple it must be exactly this way.
            if( grfMode != ( STGM_SIMPLE | STGM_READWRITE |
                             STGM_SHARE_EXCLUSIVE | STGM_CREATE ) )
                olErr( EH_Err, STG_E_INVALIDFLAG );
        }
        else
        {
            // If you Open Simple it must be one of these two ways.
            if( grfMode != (STGM_SIMPLE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE )
             && grfMode != (STGM_SIMPLE | STGM_SHARE_EXCLUSIVE | STGM_READ ) )
                olErr( EH_Err, STG_E_INVALIDFLAG );
        }
    }

    if( !fCreateAPI )
    {
        if (grfMode & STGM_DELETEONRELEASE)
            olErr(EH_Err, STG_E_INVALIDFUNCTION);

        if (grfMode & (STGM_CREATE | STGM_CONVERT))
            olErr (EH_Err, STG_E_INVALIDPARAMETER);
    }
EH_Err:
    return sc;
}

//+---------------------------------------------------------------------------
//
//  Function:   ValidateStgOptions
//
//  Synopsis:   Sanity checking for the StgOptions
//
//  Arguments:  [pStgOptions] -- options to check
//              [stgfmt] -- intended storage format
//
//  Returns:    Appropriate status code
//
//  History:    30-Mar-98   HenryLee    Created
//
//----------------------------------------------------------------------------

SCODE ValidateStgOptions (STGOPTIONS * pStgOptions, DWORD stgfmt, BOOL fCreate)
{
#ifdef LARGE_DOCFILE
    HRESULT sc = S_OK;

    olChk(ValidatePtrBuffer(pStgOptions));
    if (pStgOptions->usVersion > STGOPTIONS_VERSION ||
        pStgOptions->usVersion == 0 ||
        pStgOptions->reserved != 0)

    {
        olErr (EH_Err, STG_E_INVALIDPARAMETER);
    }

    if (fCreate)
    {
        // enable large sector support only for docfiles
        if (pStgOptions->ulSectorSize != 512 && stgfmt != STGFMT_DOCFILE)
        {
            olErr (EH_Err, STG_E_INVALIDPARAMETER);
        }

        if (pStgOptions->ulSectorSize != 512 &&
            pStgOptions->ulSectorSize != 4096)
        /*  pStgOptions->ulSectorSize != 8192 &&   */
        /*  pStgOptions->ulSectorSize != 16384 &&  */
        /*  pStgOptions->ulSectorSize != 32768)    */
        {
            olErr (EH_Err, STG_E_INVALIDPARAMETER);
        }

        if (pStgOptions->usVersion >= 2)
        {
            if (stgfmt != STGFMT_DOCFILE)
            {
                olErr (EH_Err, STG_E_INVALIDPARAMETER);
            }
            else if (pStgOptions->pwcsTemplateFile != NULL)
                olChk (ValidatePtrBuffer (pStgOptions->pwcsTemplateFile));
        }
    }
    else
    {
        if (stgfmt != STGFMT_DOCFILE)
            olErr (EH_Err, STG_E_INVALIDPARAMETER);
        if (pStgOptions->usVersion >= 2 && pStgOptions->pwcsTemplateFile !=NULL)
            olErr (EH_Err, STG_E_INVALIDPARAMETER);
    }
EH_Err:
#else
    HRESULT sc = STG_E_INVALIDPARAMETER;
#endif

    return sc;
}


//+---------------------------------------------------------------------------
//
//  Member: GetNFFTempName, public
//
//  Synopsis:   returns a filename for temporary NSS files
//
//  Arguments:  [pwcsFileName] - original file name
//              [ppwcsTmpName] - output temporary name
//
//  Returns:    Appropriate status code
//
//  History:    01-Jul-97   HenryLee    Created
//
//----------------------------------------------------------------------------

HRESULT GetNFFTempName (const WCHAR *pwcsFileName, WCHAR *awcsTmpName)
{
    HRESULT sc = S_OK;
    WCHAR awcsDir[_MAX_PATH];
    WCHAR *pwcsFile = NULL;

    //
    // Create a temp file in pwcsFileName's directory
    //
    if (pwcsFileName != NULL)
    {
        if (GetFullPathNameW (pwcsFileName, _MAX_PATH, awcsDir, &pwcsFile) == 0)
        {
            const DWORD dwErr = GetLastError();

            //In some circumstances (name == " ", for instance),
            // GetFullPathNameW can return 0 and GetLastError returns 0.
            // We want to return STG_E_INVALIDNAME for these.

            olErr(EH_Err, (dwErr != NOERROR) ? Win32ErrorToScode(dwErr) :
                                                 STG_E_INVALIDNAME);
        }
        else if (pwcsFile) *pwcsFile = L'\0';
    }
    else
    {
        DWORD cch;
        // Create a temp file for StgCreateDocfile (NULL name)
        //
        // try %tmp%, %temp%
        cch = GetTempPath(_MAX_PATH, awcsDir);
        if(0 == cch || cch > _MAX_PATH)
        {
            // finally use current directory
            awcsDir[0] = L'.';
            awcsDir[1] = L'\0';
        }
    }

    if (GetTempFileNameW (awcsDir, TEMPFILE_PREFIX, 0, awcsTmpName)==FALSE)
        olErr (EH_Err, Win32ErrorToScode(GetLastError()));

EH_Err:
    return sc;
}