/*++

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

Module Name:

    addprn.cxx

Abstract:

    Add Printer Connection UI

Author:

    Steve Kiraly (SteveKi)  10-Feb-1997

Revision History:

--*/

#include "precomp.hxx"
#pragma hdrstop

#include "asyncdlg.hxx"
#include "addprn.hxx"
#include "browse.hxx"

/*++

Name:

    PrintUIGetPrinterInformation

Routine Description:

    This function gets the information from a connected network printer,
    which includes: 

    1. The real printer name
    2. The printer comment
    3. The printer location
    4. The printer share name

Arguments:

    hPrinter            - Handle to a valid printer conenction
    pstrPrinterName     - Where to place the real printer name
    pstrComment         - Where to place the comment
    pstrLocation        - Where to place the location
    pstrShareName       - Where to place the share name

Return Value:

    TRUE the information is extracted. 
    FALSE if an error occurred.

--*/
BOOL
PrintUIGetPrinterInformation(
    IN HANDLE   hPrinter,
    IN TString *pstrPrinterName,
    IN TString *pstrComment,
    IN TString *pstrLocation,
    IN TString *pstrShareName
    )
{
    TStatusB bStatus;
    bStatus DBGNOCHK = FALSE;

    //
    // If the printer connection is ok
    // name string pointer.  
    //
    if( hPrinter )
    {
        PPRINTER_INFO_2 pInfo2  = NULL;
        DWORD           cbInfo2 = 0;

        //
        // Get the current printer info 2.
        //
        bStatus DBGCHK = VDataRefresh::bGetPrinter( hPrinter, 2, (PVOID*)&pInfo2, &cbInfo2 );

        if( bStatus )
        {
            if( pstrPrinterName )
                bStatus DBGCHK = pstrPrinterName->bUpdate( pInfo2->pPrinterName );

            if( pstrLocation )
                bStatus DBGCHK = pstrLocation->bUpdate( pInfo2->pLocation );

            if( pstrComment )
                bStatus DBGCHK = pstrComment->bUpdate( pInfo2->pComment );

            if( pstrShareName )
                bStatus DBGCHK = pstrShareName->bUpdate( pInfo2->pShareName );
        }

        //
        // Release the printer info 2 structure.
        //
        FreeMem( pInfo2 );
    }

    return bStatus;
}

/*++

Name:

    PrintUIAddPrinterConnectionUIEx

Routine Description:

    This function adds a printer connection on this machine
    to the specified printer.  This routine will return the real
    printer name if the connection is established.  Note a printer
    connection can be added using the share name, thus this routine
    will build the connection and then convert the share name to
    the real printer name.

Arguments:

    hWnd            - Parent window for any error UI.
    pszPrinter      - Printer name (e.g., "\\test\My HP LaserJet IIISi").
    pstrPrinterName - Where to place the real printer name
                      This is used when a user connects using a share name
                      but the caller needs the real printer name to set
                      the default printer.  This parameter can be NULL if the
                      real printer name is not needed.
    pstrComment     - Where to place the comment
    pstrLocation    - Where to place the location
    pstrShareName   - Where to place the share name

Return Value:

    TRUE the printer connection was added. FALSE if an error occurred.

--*/
BOOL
PrintUIAddPrinterConnectionUIEx(
    IN HWND     hwnd,
    IN LPCTSTR  pszPrinter,
    IN TString *pstrPrinterName,
    IN TString *pstrComment,
    IN TString *pstrLocation,
    IN TString *pstrShareName
    )
{
    DBGMSG( DBG_TRACE, ( "PrintUIAddPrinterConnectionUI\n" ) );
    DBGMSG( DBG_TRACE, ( "PrintUIAddPrinterConnectionUI pszPrinter " TSTR "\n", DBGSTR( pszPrinter ) ) );

    SPLASSERT( pszPrinter );

    BOOL bReturn    = FALSE;
    BOOL bDummy     = FALSE;
    HANDLE hPrinter = NULL;

    //
    // Currently just call the add printer connection UI
    //
    hPrinter = AddPrinterConnectionUI( hwnd, pszPrinter, &bDummy );

    //
    // If the connection was built and the called passed a valid printer
    // name string pointer.  If we cannot fetch the real printer name
    // we do not fail this call.
    //
    if( hPrinter )
    {
        //
        // Extract the printer information
        //
        PrintUIGetPrinterInformation( hPrinter, pstrPrinterName, pstrComment, pstrLocation, pstrShareName );

        //
        // Release the printer handle.
        //
        ClosePrinter( hPrinter );
        bReturn = TRUE;
    }

    return bReturn;
}


/*++

Name:

    PrintUIAddPrinterConnectionUI

Routine Description:

    This function adds a printer connection on this machine
    to the specified printer.  This function will bring up the
    up progress UI while the printer connection is being added.

Arguments:

    hWnd                - Parent window for progress UI.
    pszPrinter          - Printer name (e.g., "\\test\My HP LaserJet IIISi").
    bShowConnectionUI   - TRUE show connection UI, FALSE do not show connection UI.

Return Value:

    TRUE the printer connection was added. FALSE if progress UI was canceled or
    the printer connection could not be added.

Note:

    The last error is set on failure if the dialog was canceled.

--*/

BOOL
PrintUIAddPrinterConnectionUI(
    IN HWND    hwnd,
    IN LPCTSTR pszPrinter,
    IN BOOL    bShowConnectionUI
    )
{
    //
    // Assume failure.
    //
    BOOL bReturn = FALSE;

    //
    // Allocate the add printer connection data.
    //
    auto_ptr<TAddPrinterConnectionData> pInfo = new TAddPrinterConnectionData;

    //
    // If add printer connection data was returned.
    //
    if( pInfo.get() )
    {
        pInfo->_strPrinter.bUpdate( pszPrinter );
        pInfo->_bShowConnectionUI = bShowConnectionUI;

        //
        // Create the asynchrnous dialog.
        //
        TAsyncDlg *pDlg = new TAsyncDlg( hwnd, pInfo.get(), DLG_ASYNC );

        if( pDlg )
        {
            //
            // Aquire the refrence lock this will increment the refrence count
            // of the async dialog.  The refrence lock will then be decremented
            // when the reflock fall out of scope.
            //
            TRefLock<TAsyncDlg> pAsyncDlg( pDlg );

            //
            // Create the dialog title.
            //
            TString strTitle;
            TCHAR szText[kStrMax+kPrinterBufMax];
            UINT nSize = COUNTOF( szText );
            strTitle.bLoadString( ghInst, IDS_CONNECTING_TO_PRINTER );
            ConstructPrinterFriendlyName( pszPrinter, szText, &nSize );
            strTitle.bCat( szText );

            //
            // Set the dialog title.
            //
            pDlg->vSetTitle( strTitle );

            //
            // Display the dialog.
            //
            INT iRetval = pDlg->bDoModal();

            //
            // If the dialog exited normally,
            // The use did not cancel the dialog then set the return
            // value.
            //
            if( iRetval == IDOK )
            {
                bReturn = pInfo.get()->_ReturnValue;
            }

            //
            // If the user cancel the dialog set the last error.
            //
            if( iRetval == IDCANCEL )
            {
                SetLastError( ERROR_CANCELLED );
            }

            //
            // Release the pinfo stucture to the Asynchronous dialog class.
            //
            pInfo.release();

        }
    }
    return bReturn;
}


/*++

Name:

    PrintUIAddPrinterConnection

Routine Description:

    This routine is very similar to the AddPrinterConnection in the clien
    side of spooler, however if the users current domain is the same
    as the domain name in the printer name then the short printer name is
    used.  If this routine returns success then the printer new printer
    name is returned in pstrConnection.

Arguments:

    pszConnection - pointer to full printer connection name (UNC name)
    pstrConnection - optional pointer to string object where to return
                    the new printer name if it was shortened.

Return Value:

    TRUE success, FALSE error occurred.

--*/

BOOL
PrintUIAddPrinterConnection(
    IN LPCTSTR  pszConnection,
    IN TString  *pstrConnection OPTIONAL
    )
{
    SPLASSERT( pszConnection );

    TStatusB    bStatus;
    TString     strConnection;

    //
    // Do a quick check to see if the printer name has a minimal potential
    // of containing a domain name.
    //
    if( _tcschr( pszConnection, _T('.') ) )
    {
        TString strDomainName;

        //
        // Get the current users domain name, we do not fail if the domain
        // name cannot be fetched, just add the connection. Maybe we are not
        // logged on to a domain.
        //
        bStatus DBGCHK = GetDomainName( strDomainName );

        //
        // Convert the printer domain name to a short name.
        //
        bStatus DBGCHK = ConvertDomainNameToShortName( pszConnection, strDomainName, strConnection );

        //
        // Get a pointer to the printer name, the name may have been shortened.
        //
        if( bStatus )
        {
            pszConnection = strConnection;
        }
    }

    //
    // Display a debug message see what the printer name really is.
    //
    DBGMSG( DBG_TRACE, ("PrintUIAddPrinterConnection " TSTR "\n", pszConnection ) );

    //
    // Add the printer connection.
    //
    bStatus DBGCHK = AddPrinterConnection( const_cast<LPTSTR>( pszConnection ) );

    if( bStatus && pstrConnection )
    {
        //
        // Copy back the printer name if a string object pointer was provided.
        //
        bStatus DBGCHK = pstrConnection->bUpdate( pszConnection );

        //
        // We were unable to make a copy of the connection string,
        // remove the printer connection and fail the call.  This
        // should never happen.
        //
        SPLASSERT( bStatus );

        if( !bStatus )
        {
            (VOID)DeletePrinterConnection( const_cast<LPTSTR>( pszConnection ) );
        }
    }

    return bStatus;
}


/*++

Name:

    ConvertDomainNameToShortName

Routine Description:

    Give a fully qualified DNS printer name convert it to a short name
    if the domain part of the printer name is identical to the domain
    name of the user is currently logged on to.  Example:
    \\server1.dns.microsoft.com\test dns.microsoft.com resultant printer
    name \\server1\test.

Arguments:

    pszPrinter - pointer to fully qualified DNS printer name.
    pszDomain  - pointer to the users domain name.
    strShort   - refrence to string where to return the short printer name.

Return Value:

    TRUE short name returned, FALSE error occurred.

--*/

BOOL
ConvertDomainNameToShortName(
    IN      LPCTSTR pszPrinter,
    IN      LPCTSTR pszDomain,
    IN OUT  TString &strShort
    )
{
    SPLASSERT( pszPrinter );
    SPLASSERT( pszDomain );

    TStatusB bStatus;

    //
    // If a valid domain and unc printer name were provided then continue
    // checking for a domain name match.
    //
    bStatus DBGNOCHK = pszDomain && *pszDomain && pszPrinter && *(pszPrinter+0) == _T('\\') && *(pszPrinter+1) == _T('\\');

    if( bStatus )
    {
        //
        // Assume failure, until we actually build a short name.
        //
        bStatus DBGNOCHK = FALSE;

        //
        // Find the first '.' this will be the start of the domain name if
        // present.  The domain name is from the first . to the first \
        //
        LPTSTR pszShort = _tcschr( pszPrinter, _T('.') );

        if( pszShort )
        {
            //
            // Allocate a printer name buffer, I simplify the is case by just allocating
            // a buffer large enought for the printer name.  I know the name will potentialy
            // smaller, but it not worth figuring out how much smaller.
            //
            LPTSTR pszBuffer = (LPTSTR)AllocMem( (_tcslen( pszPrinter ) + 1) * sizeof(TCHAR) );

            if( pszBuffer )
            {
                LPTSTR p = pszShort+1;
                LPTSTR d = pszBuffer;

                //
                // Copy the domain name from the full printer name to the allocated buffer.
                //
                while( *p && *p != _T('\\') )
                {
                    *d++ = *p++;
                }

                //
                // Null terminate the destination buffere.
                //
                *d = 0;

                //
                // Check if the domain name in the printer name match the provided domain name.
                //
                if( !_tcsicmp( pszBuffer, pszDomain ) )
                {
                    //
                    // Build the short printer name less the domain name.
                    //
                    memmove( pszBuffer, pszPrinter, sizeof(TCHAR) * (size_t)(pszShort-pszPrinter) );
                    _tcscpy( pszBuffer+(pszShort-pszPrinter), p );

                    //
                    // Copy back the short printer name to the provided string object.
                    //
                    bStatus DBGCHK = strShort.bUpdate( pszBuffer );
                }

                //
                // Release the temp printer name buffer.
                //
                FreeMem( pszBuffer );
            }
        }
    }

    return bStatus;
}

/********************************************************************

    Add Printer Connection class

********************************************************************/

/*++

Name:

    TAddPrinterConnectionData

Routine Description:

    This function is the derived add printer connection data class
    constructure that is used to pass data to an asynchronous worker
    thread.  The worker thread is call after the progress UI is displayed.

Arguments:

    None

Return Value:

    Nothing.

--*/

TAddPrinterConnectionData::
TAddPrinterConnectionData(
    VOID
    ) : _bShowConnectionUI( TRUE )
{
    DBGMSG( DBG_TRACE, ( "TAddPrinterConnectionData::ctor\n" ) );
}

/*++

Name:

    ~TAddPrinterConnectionData

Routine Description:

    Destructor.

Arguments:

    None

Return Value:

    Nothing.

Note:

    The last error is not set on failure.

--*/

TAddPrinterConnectionData::
~TAddPrinterConnectionData(
    VOID
    )
{
    DBGMSG( DBG_TRACE, ( "TAddPrinterConnectionData::dtor\n" ) );
}

/*++

Name:

    bAsyncWork

Routine Description:

    This routine is the worker thread call back routine.  It is called after the
    progress UI has been started and is currently being displayed.  Because this
    function is a virtual within the TAsyncData class any specific data needed
    by this routine must be fully contained within the derived class.  This routine
    accepts on argument a pointer to the progress dialog pointer.  If needed this
    pointer should be used to stop the progress UI if any UI must be displayed.
    The hwnd of the progress UI should be used as the parent of any windows
    this routine may need to created.

Arguments:

    Pointer to TAsyncDlg class.

Return Value:

    TRUE function completed successfully, FALSE error occurred.

--*/

BOOL
TAddPrinterConnectionData::
bAsyncWork(
    IN TAsyncDlg *pDlg
    )
{
    //
    // Print some debugging information.
    //
    DBGMSG( DBG_TRACE, ( "pszPrinter " TSTR "\n", DBGSTR( (LPCTSTR)_strPrinter ) ) );

    //
    // Currently just call the add printer connection UI in winspool.drv
    //
    if( _bShowConnectionUI )
    {
        BOOL bDummy = FALSE;

        //
        // Call add printer connection UI, note the add printer connection UI sill
        // will display UI if an error occurred or if the driver was needed in the
        // masq printer case.
        //
        HANDLE  hHandle = AddPrinterConnectionUI( pDlg->_hDlg, _strPrinter, &bDummy );

        //
        // Handle is not needed, just release it if a valid handle was returned.
        //
        if( hHandle )
        {
            ClosePrinter( hHandle );
            _ReturnValue = TRUE;
        }
        else
        {
            _ReturnValue = FALSE;
        }
    }
    else
    {
        //
        // Add printer connection will just try and make an RPC connection to the printer
        // this call will not handle the masq printer case.
        //
        _ReturnValue = PrintUIAddPrinterConnection( _strPrinter, NULL );
    }

    return TRUE;
}