/*++

Copyright (C) Microsoft Corporation, 1998 - 1999
All rights reserved.

Module Name:

    PrnPrst.cxx

Abstract:

    TPrinterPersist member definitions.

    This class implements methods for storing , restoring printer settings into a file

    Also , implements read, write , seek methods to operate a file likewise a stream

Author:

    Adina Trufinescu (AdinaTru)  4-Nov-1998

Revision History:

--*/
#include "precomp.h"
#pragma hdrstop

#include "prnprst.hxx"

/*++

Title:                  

    TPrinterPersist

Routine Description:    

    Initialise m_pPrnStream to NULL.
    TPrinterPersist functionality is implemented through this object methods call.
    m_pPrnStream is created when   BindPrinterAndFile is called. If this function fails, 
    any subsequent method call will fail too.

Arguments:  

    None

Return Value:  

    Nothing

--*/
TPrinterPersist::
TPrinterPersist()
{
    m_pPrnStream = NULL;
}

/*++

Title:                  

    ~TPrinterPersist

Routine Description:    

    destructor: if created , deletes PrnStream object

Arguments:  

    None

Return Value:  

    Nothing

--*/

TPrinterPersist::
~TPrinterPersist()
{
    if(m_pPrnStream)
    {
        delete m_pPrnStream;
    }
}

/*++

Title:                  

    BindPrinterAndFile

Routine Description:

    Create  a TPrnStream object and if succeeded , bind it to a printer and a file
                    
Arguments:

    pszPrinter  - printer to bind to
    pszFile     - file to bind to

Return Value:  

    BindPrinterAndFile could return E_OUTOFMEMORY if failed to allocate memory,
    an error code mapped to HRESULT with storage facility set if failed to open printer in TPrnStream::BindPrnStream
    or S_OK if suceeded

--*/
HRESULT
TPrinterPersist::
BindPrinterAndFile(
    IN LPCTSTR pszPrinter,
    IN LPCTSTR pszFile
    )
{
    TStatusB    bStatus;
    TStatusH    hr;

    // 
    //  Create a TPrnStream obj ant initialise it with printer name and file name
    //
    m_pPrnStream = new TPrnStream(pszPrinter, pszFile);

    bStatus DBGCHK = (m_pPrnStream != NULL);

    if(bStatus)
    {
        DBGMSG(DBG_TRACE , ("BindPrinterAndFile to : " TSTR " \n" , pszPrinter));
        //
        // calls TPrnStream bind method; it will open the printer with maximum access rights
        // and will create a TStream object that will open the file
        //
        hr DBGCHK = m_pPrnStream->BindPrnStream();

        //
        //  if failed to bind TPrnStream to printer and file , delete it!!!
        //
        if(FAILED(hr))
        {
            delete m_pPrnStream;

            m_pPrnStream = NULL;
        }

    }
    else
    {
        hr DBGCHK = E_OUTOFMEMORY;
    }

    return hr;
    

}

/*++

Title:                  

    UnBindPrinterAndFile

Routine Description:

    Call UnBindPrnStream method of m_pPrnStream.After this function is called , any subsequent method call will fail.
    
Arguments:

    None

Return Value:
    
    S_OK if succeeded
    PRN_PERSIST_ERROR error code mapped to HRESULT(FACILITY_ITF) if failed

--*/
HRESULT
TPrinterPersist::
UnBindPrinterAndFile(
    )
{
    TStatusH    hr;

    DBGMSG(DBG_TRACE , ("UnBindPrinterAndFile!!! \n"));

    hr DBGNOCHK = E_FAIL;

    //
    //  if BindPrinterAndFile wasn't called or failed , this funct will fail also
    //
    if(m_pPrnStream != NULL)
    {
        hr DBGCHK = m_pPrnStream->UnBindPrnStream();
    }


    return hr;
}

/*++

Title:                  

    bGetPrinterAndFile

Routine Description:

    Query m_pPrnStream for printer and file it is binded

Arguments: 

    pszPrinter  -   outs printer name
    pszFile     -   outs file name

Return Value:  

    TRUE if m_pPrnStream is initialized 
    FALSE otherwise

--*/
BOOL
TPrinterPersist::   
bGetPrinterAndFile(
    OUT LPCTSTR& pszPrinter,
    OUT LPCTSTR& pszFile
    )
{
    TStatusB    bStatus;

    if(m_pPrnStream != NULL)
    {
        pszPrinter   = m_pPrnStream->GetPrinterName();

        pszFile      = m_pPrnStream->GetFileName();

        bStatus DBGNOCHK = TRUE;
    }
    else
    {
        bStatus DBGCHK = FALSE;
    }

    return bStatus;

}
/*++

Title:                  

    Read

Routine Description:

    Indirects call to PrnStream which , in turns , idirects it to TStream  
    Eventually , this function will be called through IStream interface
    Arguments the same as IStream::Read
                    
Arguments: 

    pv  -   The buffer that the bytes are read into
    cb  -   The offset in the stream to begin reading from.
    pcbRead -   The number of bytes to read

Return Value:

    S_OK if succeeded
    ReadFile Win32 error mapped to HRESULT(FACILITY_STORAGE) if failed

--*/
HRESULT
TPrinterPersist::
Read(                                
    IN VOID * pv,      
    IN ULONG  cb,       
    IN ULONG * pcbRead 
    )
{
    if(m_pPrnStream)
    {
        return m_pPrnStream->GetIStream()->Read(pv, cb, pcbRead);
    }
    else
    {
        return STG_E_NOMOREFILES;
    }
    
}
/*++

Title:                  

    Write

Routine Description:

    Indirects call to PrnStream which , in turns , idirects it to TStream  
    Eventually , this function will be called through IStream interface
    Arguments the same as IStream::Write
                    
Arguments:  

    pv  -   The buffer to write from.
    cb  -   The offset in the array to begin writing from
    pcbRead -   The number of bytes to write

Return Value:

    S_OK if succeeded
    WriteFile Win32 error mapped to HRESULT(FACILITY_STORAGE) if failed

--*/
HRESULT
TPrinterPersist::
Write(                            
    IN VOID     const* pv,  
    IN ULONG    cb,
    IN ULONG*   pcbWritten 
    )
{
    if(m_pPrnStream)
    {
        return m_pPrnStream->GetIStream()->Write(pv, cb, pcbWritten);
    }
    else
    {
        return STG_E_NOMOREFILES;
    }
}

/*++

Title:                  

    Seek

Routine Description:

    Indirects call to PrnStream which , in turns , idirects it to TStream  
    Arguments the same as IStream::Seek
    Eventually , this function will be called through IStream interface
                    
Arguments: 

    dlibMove        -   The offset relative to dwOrigin
    dwOrigin        -   The origin of the offset
    plibNewPosition -   Pointer to value of the new seek pointer from the beginning of the stream  

Return Value:  

    S_OK if succeeded
    SetFilePointer Win32 error mapped to HRESULT(FACILITY_STORAGE) if failed

--*/
HRESULT
TPrinterPersist::
Seek(                                
    IN LARGE_INTEGER dlibMove,  
    IN DWORD dwOrigin,          
    IN ULARGE_INTEGER * plibNewPosition 
    )
{
    if(m_pPrnStream)
    {
        return m_pPrnStream->GetIStream()->Seek(dlibMove, dwOrigin, plibNewPosition);
    }
    else
    {
        return STG_E_NOMOREFILES;
    }
}


/*++

Title:                  

    bStorePrinterInfo

Routine Description:    

    Writes the printer settings into file.
    Depending on specified flags, call TPrnStream   methods to store printer data
    This function must be called after calling BindPrinterAndFile which bind the object to 
    a printer and to a file.For that fnct to succeed, current user must have PRINTER_READ rights
    on specified printer.

Arguments:  

    Flags       -   specifies what settings should be stored
    StoredFlags -   specifies what settings were actually stored

Return Value:

    S_OK if succeeded
    PRN_PERSIST_ERROR error code mapped to HRESULT(FACILITY_ITF) if failed

--*/
HRESULT
TPrinterPersist::
StorePrinterInfo(
    IN  DWORD   Flags,
    OUT DWORD&  StoredFlags
    )
{
    TStatusH    hr;

    DBGMSG(DBG_TRACE , ("Store Printer Flag %x \n" , Flags));

    hr DBGNOCHK = E_FAIL;

    //
    //  if BindPrinterAndFile wasn't called or failed , this funct will fail also
    //
    if(m_pPrnStream != NULL)
    {
        hr DBGCHK = m_pPrnStream->StorePrinterInfo(Flags, StoredFlags);
    }


    DBGMSG( DBG_TRACE, ( "Store Completed :: %x \n" ,hr));


    return hr;

}


/*++

Title:                  

    RestorePrinterInfo

Routine Description:    

    Depending on specified flags, call TPrnStream   methods to restore printer data
    This function must be called after calling BindPrinterAndFile which bind the object to 
    a printer and to a file.For that fnct to succeed, current user has to have PRINTER_ALL_ACCESS rights.
    If he don't , BindPrinterAndFile will bind to printer with PRINTER_READ which will lead to 
    SetPrinter failure. 
    
Arguments:              

    Flags         -   specifies what settings should be restored
    RestoredFlags -   specifies what settings were actually restored

Return Value:
    
    S_OK if succeeded
    PRN_PERSIST_ERROR error code mapped to HRESULT(FACILITY_ITF) if failed


--*/
HRESULT
TPrinterPersist::
RestorePrinterInfo(
    IN DWORD    Flags,
    OUT DWORD&  RestoredFlags
    )
{
    TStatusH            hr;
    TStatusB            bStatus;
    
    hr DBGNOCHK = E_FAIL;



    RestoredFlags = 0;

    DBGMSG(DBG_TRACE , ("Restore Printer info %x \n" , Flags));

    //
    //  if BindPrinterAndFile wasn't called or failed , this funct will fail also
    //
    if(m_pPrnStream != NULL)
    {
        hr DBGCHK = m_pPrnStream->CheckPrinterNameIntegrity(Flags);

        if(SUCCEEDED(hr))
        {
            DBGMSG( DBG_TRACE, ( "CheckPrinterNameIntegrity OK!!! %x \n" , hr));

            hr DBGCHK = m_pPrnStream->RestorePrinterInfo(Flags, RestoredFlags);
        }

    }

    DBGMSG( DBG_TRACE, ( "RESTORE END!!! %x \n" , hr));
    return hr;

}

/*++

Title:                  

    SafeRestorePrinterInfo

Routine Description:    

    Before restoring ant flags, create a new temporary persistent object binded to also to printer which stores current printer settings. 
    If something fails at the restoring, we are able to roll back all the changes made and we will leave printer in the 
    initial state.
    A temporary file which holds initial printer settings are created by invoking StorePrinterInfo for temporary object.
    This file will be deleted by calling UnBindPrinterAndFile for temporary object.

Arguments:              

    Flags         -   specifies what settings should be restored
    
Return Value:
    
    S_OK if succeeded
    PRN_PERSIST_ERROR error code mapped to HRESULT(FACILITY_ITF) if failed


--*/
HRESULT
TPrinterPersist::
SafeRestorePrinterInfo(
    IN DWORD    Flags
    )
{
    LPTSTR  pszPrinterTemp;
    LPTSTR  pszFile;
    TString strFileTempPrefix(_T("Prst"));
    TCHAR   szTempFileName[MAX_PATH];
    TCHAR   szTempPath[MAX_PATH];


    TStatusH hr;
    TStatusH hrBkp;

    DWORD StoredFlags;
    DWORD RestoredFlags;

    //
    // initialize hr to Backup error; if something wrong happens when trying to build backup version of file,
    // fnct will return Back-up Error
    //
    hr  DBGNOCHK     = MakePrnPersistHResult(PRN_PERSIST_ERROR_BACKUP);

    hrBkp DBGNOCHK   = MakePrnPersistHResult(PRN_PERSIST_ERROR_BACKUP);

    //
    // Create temporary object for storing current settings
    // In case the main restoring fails , current settings can be restored
    //
    TPrinterPersist* pPrnPersistTemp = new TPrinterPersist;

    if(pPrnPersistTemp != NULL)
    {
        if(bGetPrinterAndFile(pszPrinterTemp, pszFile))
        {
            TStatus Status;
            //
            // create a temp file for saving current settings
            //
            Status DBGNOCHK = GetTempPathW(MAX_PATH, szTempPath); 
            if (Status > 0 && Status <= MAX_PATH) {
                GetTempFileName(szTempPath, strFileTempPrefix, 0, szTempFileName);
            }
            else {
                GetTempFileName(_T("."), strFileTempPrefix, 0, szTempFileName);
            }
            
            hrBkp DBGCHK = pPrnPersistTemp->BindPrinterAndFile(pszPrinterTemp, szTempFileName);

            if(SUCCEEDED(hrBkp))
            {
                hrBkp DBGCHK = pPrnPersistTemp->StorePrinterInfo(Flags, StoredFlags);

                if(SUCCEEDED(hrBkp))
                {
                    hr DBGCHK = RestorePrinterInfo(Flags, RestoredFlags);

                    if(FAILED(hr))
                    {
                        //
                        // if main restoring failed , try backup restoring with flags set to:
                        //      flags successfully restored
                        //      &
                        //      flags that were successfully stored in backup file
                        // fct will still return hr = reason for main restoring failure
                        //
                        // PRST_FORCE_NAME must be always set on force for a scenario like this:
                        // restore settings from file ( printer P1 stored) on top of printer P2 with f flag set
                        // restoring fails (printer P2 could become P1 at this point) and back-up is needed; 
                        // in back-up file , printer name is P2 . This means we have to force printer name from back-up file,
                        // in order to become P2 again
                        // If it's not the case then printer was renamed and the restoring failed,f flag is harmless
                        //
                        hrBkp DBGCHK = pPrnPersistTemp->RestorePrinterInfo((StoredFlags & RestoredFlags) | PRST_FORCE_NAME , RestoredFlags);

                        if(FAILED(hrBkp))
                        {
                            // if backup restoring failed also,
                            // set hr to FATAL Error
                            //
                            hr DBGCHK = MakePrnPersistHResult(PRN_PERSIST_ERROR_FATAL);
                        }

                    }

                }

            }

            pPrnPersistTemp->UnBindPrinterAndFile();

        }

        delete pPrnPersistTemp;
    }

    return hr;
}

/*++

Title:                  

    QueryPrinterInfo

Routine Description:    

    Read from 
                    
Arguments:              

    Flags       -   specifies what settings should be restored
    pPrstInfo   -   pointer to union that holds a pointer to read item

Return Value:

    S_OK if succeeded;
    ERROR_INVALID_PARAMETER maped to HRESULT if more than one flag specified;
    PRN_PERSIST_ERROR error code mapped to HRESULT(FACILITY_ITF) if failed;

--*/
HRESULT
TPrinterPersist::
QueryPrinterInfo(
    IN  PrinterPersistentQueryFlag           Flag,
    OUT PersistentInfo                      *pPrstInfo
    )
{
    TStatusH            hr;
    TStatusB            bStatus;
    
    hr DBGNOCHK = E_FAIL;



    DBGMSG(DBG_TRACE , ("Restore Printer info %x \n" , Flag));

    //
    //  if BindPrinterAndFile wasn't called or failed , this funct will fail also
    //
    if(m_pPrnStream != NULL)
    {
        hr DBGCHK = m_pPrnStream->QueryPrinterInfo(Flag, pPrstInfo);
    
    }

    DBGMSG( DBG_TRACE, ( "QueryPrinterInfo END!!! %x \n" , hr));

    return hr;

}