/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    nwshprop.cxx

Abstract:

    This module implements the property pages of shell extension classes.

Author:

    Yi-Hsin Sung (yihsins)  25-Oct-1995

--*/

extern "C"
{
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>

#include <commctrl.h>
#include <shellapi.h>
#include <shlobj.h>
#define DONT_WANT_SHELLDEBUG
#include <shsemip.h>

#include <nwapi32.h>
#include <ndsapi32.h>
#include <nwmisc.h>
#include  <nds.h>
#include "nwshrc.h"
#include "nwutil.h"
#include "drawpie.h"
}

#include "nwshcmn.h"
#include "nwshext.h"


LPWSTR WINAPI AddCommas( DWORD dw, LPWSTR pszResult, DWORD dwSize );
LPWSTR WINAPI ShortSizeFormat64( ULONGLONG dw64, LPWSTR szBuf );

#define NAMESPACE_DOS       0
#define NAMESPACE_MAC       1
#define NAMESPACE_UNIX      2
#define NAMESPACE_FTAM      3
#define NAMESPACE_OS2       4


BOOL
CALLBACK
NDSPage_DlgProc(
    HWND hDlg,
    UINT uMessage,
    WPARAM wParam ,
    LPARAM lParam
);

BOOL
CALLBACK
NWPage_DlgProc(
    HWND hDlg,
    UINT uMessage,
    WPARAM wParam ,
    LPARAM lParam
);

//
//  FUNCTION: CNWObjContextMenu::AddPages(LPFNADDPROPSHEETPAGE, LPARAM)
//
//  PURPOSE: Called by the shell just before the property sheet is displayed.
//
//  PARAMETERS:
//    lpfnAddPage -  Pointer to the Shell's AddPage function
//    lParam      -  Passed as second parameter to lpfnAddPage
//
//  RETURN VALUE:
//
//    NOERROR in all cases.  If for some reason our pages don't get added,
//    the Shell still needs to bring up the Properties... sheet.
//
//  COMMENTS:
//

STDMETHODIMP CNWObjContextMenu::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
{
    LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;

    if ( !::GetNetResourceFromShell( _pDataObj, pNetRes, sizeof( _buffer )))
    {
        // We could not get the net resource of the current object,
        // hence we could not add the property pages
        return NOERROR;
    }

    DWORD dwDialogId = 0;
    BOOL  fIsNds = NwIsNdsSyntax( pNetRes->lpRemoteName );

    switch ( pNetRes->dwDisplayType )
    {
        case RESOURCEDISPLAYTYPE_SERVER:
            dwDialogId = DLG_SERVER_SUMMARYINFO;
            break;

        case RESOURCEDISPLAYTYPE_NDSCONTAINER:
            break;

        case RESOURCEDISPLAYTYPE_TREE:
            // We need to set fIsNds to TRUE since a tree name "\\tree" 
            // does not look like a NDS name
            // and hence NwIsNDsSyntax will return FALSE.
            fIsNds = TRUE;
            break;

        case RESOURCEDISPLAYTYPE_SHARE:
            if ( pNetRes->dwType == RESOURCETYPE_PRINT )
                dwDialogId = DLG_PRINTER_SUMMARYINFO;
            else
                dwDialogId = DLG_SHARE_SUMMARYINFO;
            break;

        case RESOURCEDISPLAYTYPE_ROOT:
        case RESOURCEDISPLAYTYPE_NETWORK:
        default:
            // No property page need to be added here. Just return success.
            return NOERROR;
    }

    if ( dwDialogId != 0 )
    {
        FillAndAddPage( lpfnAddPage, lParam,
                        (DLGPROC) ::NWPage_DlgProc,
                        MAKEINTRESOURCE( dwDialogId ));
    }

    // NOTE: Do we need to add another property page contain admin tools
    // for chicago peer servers? Probably not!

    if ( fIsNds )
    {
        FillAndAddPage( lpfnAddPage, lParam,
                        (DLGPROC) ::NDSPage_DlgProc,
                        pNetRes->dwDisplayType == RESOURCEDISPLAYTYPE_TREE ?
                            MAKEINTRESOURCE( DLG_NDS_SUMMARYINFO) :
                            MAKEINTRESOURCE( DLG_NDSCONT_SUMMARYINFO));

        // NOTE: Need to add a page for system policy here if the user has admin privileges
        // in the NDS tree.
    }

    return NOERROR;
}

//
//  FUNCTION: CNWObjContextMenu::ReplacePage(UINT, LPFNADDPROPSHEETPAGE, LPARAM)
//
//  PURPOSE: Called by the shell only for Control Panel property sheet
//           extensions
//
//  PARAMETERS:
//    uPageID         -  ID of page to be replaced
//    lpfnReplaceWith -  Pointer to the Shell's Replace function
//    lParam          -  Passed as second parameter to lpfnReplaceWith
//
//  RETURN VALUE:
//
//    E_NOTIMPL, since we don't support this function.  It should never be
//    called.

//  COMMENTS:
//

STDMETHODIMP CNWObjContextMenu::ReplacePage(UINT uPageID,
                                    LPFNADDPROPSHEETPAGE lpfnReplaceWith,
                                    LPARAM lParam)
{
    return E_NOTIMPL;
}

VOID CNWObjContextMenu::FillAndAddPage( LPFNADDPROPSHEETPAGE lpfnAddPage,
                                        LPARAM  lParam,
                                        DLGPROC pfnDlgProc,
                                        LPWSTR  pszTemplate )
{
    PROPSHEETPAGE psp;
    HPROPSHEETPAGE hpage;

    psp.dwSize      = sizeof(psp);  // no extra data.
    psp.dwFlags     = PSP_USEREFPARENT;
    psp.hInstance   = ::hmodNW;
    psp.pfnDlgProc  = pfnDlgProc;
    psp.pcRefParent = (UINT *) &g_cRefThisDll;
    psp.pszTemplate = pszTemplate;
    psp.hIcon       = 0;
    psp.pszTitle    = NULL;
    psp.pfnCallback = NULL;

    psp.lParam      = (LPARAM) this;
    this->AddRef();

    hpage = CreatePropertySheetPage(&psp);

    if (hpage)
    {
        if (!lpfnAddPage(hpage, lParam))
            DestroyPropertySheetPage(hpage);
    }

}

// The following are arrays of help contexts for the property dialogs

static DWORD aServerIds[] = { IDD_SERVER_NAME        ,IDH_SERVERNAME,
                              IDD_SERVER_COMMENT_TXT ,IDH_COMMENT,
                              IDD_SERVER_COMMENT     ,IDH_COMMENT,
                              IDD_SERVER_VERSION_TXT ,IDH_VERSION,
                              IDD_SERVER_VERSION     ,IDH_VERSION,
                              IDD_SERVER_REVISION_TXT,IDH_REVISION,
                              IDD_SERVER_REVISION    ,IDH_REVISION,
                              IDD_SERVER_CONNECT_TXT ,IDH_CONNINUSE,
                              IDD_SERVER_CONNECT     ,IDH_CONNINUSE,
                              IDD_SERVER_MAXCON_TXT  ,IDH_MAXCONNS,
                              IDD_SERVER_MAXCON      ,IDH_MAXCONNS,
                              0, 0 };

static DWORD aPrinterIds[] = { IDD_PRINTER_NAME,       IDH_PRINTER_NAME,
                               IDD_PRINTER_QUEUE_TXT,  IDH_PRINTER_QUEUE,
                               IDD_PRINTER_QUEUE,      IDH_PRINTER_QUEUE,
                               0, 0 };

static DWORD aNDSIds[] = { IDD_NDS_NAME_TXT,    IDH_NDS_NAME,
                           IDD_NDS_NAME,        IDH_NDS_NAME,
                           IDD_NDS_CLASS_TXT,   IDH_NDS_CLASS,
                           IDD_NDS_CLASS,       IDH_NDS_CLASS,
                           IDD_NDS_COMMENT_TXT, IDH_NDS_COMMENT,
                           IDD_NDS_COMMENT,     IDH_NDS_COMMENT,
                           0, 0 };

static DWORD aShareIds[] = { IDD_SHARE_NAME,        IDH_SHARE_NAME,
                             IDD_SHARE_SERVER_TXT,  IDH_SHARE_SERVER,
                             IDD_SHARE_SERVER,      IDH_SHARE_SERVER,
                             IDD_SHARE_PATH_TXT,    IDH_SHARE_PATH,
                             IDD_SHARE_PATH,        IDH_SHARE_PATH,
                             IDD_SHARE_USED_SPC_CLR,IDH_SHARE_USED_SPC,
                             IDD_SHARE_USED_SPC_TXT,IDH_SHARE_USED_SPC,
                             IDD_SHARE_USED_SPC,    IDH_SHARE_USED_SPC,
                             IDD_SHARE_USED_SPC_MB, IDH_SHARE_USED_SPC,
                             IDD_SHARE_FREE_SPC_CLR,IDH_SHARE_FREE_SPC,
                             IDD_SHARE_FREE_SPC_TXT,IDH_SHARE_FREE_SPC,
                             IDD_SHARE_FREE_SPC,    IDH_SHARE_FREE_SPC,
                             IDD_SHARE_FREE_SPC_MB, IDH_SHARE_FREE_SPC,
                             IDD_SHARE_MAX_SPC_TXT, IDH_SHARE_MAX_SPC,
                             IDD_SHARE_MAX_SPC,     IDH_SHARE_MAX_SPC,
                             IDD_SHARE_MAX_SPC_MB,  IDH_SHARE_MAX_SPC,
                             IDD_SHARE_PIE,         IDH_SHARE_PIE,
                             IDD_SHARE_LFN_TXT,     IDH_SHARE_LFN_TXT,
                             0,0 };


#if 0
static DWORD aWGIds[] = { IDD_WRKGRP_NAME,       IDH_WRKGRP_NAME,
                          IDD_WRKGRP_TYPE_TXT,   IDH_WRKGRP_TYPE,
                          IDD_WRKGRP_TYPE,       IDH_WRKGRP_TYPE,
                          0, 0 };

static DWORD aNDSAdminIds[] = { IDD_ENABLE_SYSPOL,  IDH_ENABLE_SYSPOL,
                                IDD_VOLUME_LABEL,   IDH_VOLUME_LABEL,
                                IDD_VOLUME,         IDH_VOLUME,
                                IDD_DIRECTORY_LABEL,IDH_DIRECTORY_LABEL,
                                IDD_DIRECTORY,      IDH_DIRECTORY,
                                0, 0 };
#endif


void NDSPage_InitDialog(
    HWND hDlg,
    LPPROPSHEETPAGE psp
    )
{
    CNWObjContextMenu*  pPSClass = (CNWObjContextMenu*)psp->lParam;
    LPNETRESOURCE       pnr = NULL;
    DWORD               err = NO_ERROR;
    NTSTATUS            ntstatus = STATUS_SUCCESS;
    HANDLE              hTreeConn = NULL;

    if ( pPSClass )
        pnr = pPSClass->QueryNetResource();

    if ( pnr == NULL )
    {
        ASSERT(FALSE);

        // This should not happen. We can always get the net resource which is queried
        // during AddPages.
        return;
    }

    do {  // not a loop, just wanted to break on error

        LPWSTR pszRemoteName = pnr->lpRemoteName;

        if ( pszRemoteName[0] == L' ')   // tree names have a space in front " \\mardev"
            pszRemoteName++;

        if ( pnr->dwDisplayType == RESOURCEDISPLAYTYPE_TREE )
        {
            SetDlgItemText( hDlg, IDD_NDS_NAME, pszRemoteName + 2); // get past backslashes
        }
        else
        {
            SetDlgItemText( hDlg, IDD_NDS_NAME, wcschr( pszRemoteName + 2, L'\\') + 1);
        }

        DWORD dwOid;

        err = NwOpenAndGetTreeInfo( pszRemoteName,
                                    &hTreeConn,
                                    &dwOid );

        if ( err != NO_ERROR )
        {
            break;
        }

        BYTE  RawResponse[TWO_KB];
        DWORD RawResponseSize = sizeof(RawResponse);

        ntstatus = NwNdsReadObjectInfo( hTreeConn,
                                        dwOid,
                                        RawResponse,
                                        RawResponseSize );

        if ( !NT_SUCCESS( ntstatus ))
        {
            err = RtlNtStatusToDosError(ntstatus);
            break;
        }

        LPBYTE pObjectClass = RawResponse;

        pObjectClass += sizeof( NDS_RESPONSE_GET_OBJECT_INFO ) + sizeof(DWORD);

        ::SetDlgItemText( hDlg, IDD_NDS_CLASS, (LPWSTR) pObjectClass );

        // NOTE: The description can only be read successfully with administrative privilege

        DWORD iterHandle = (DWORD) -1;
        UNICODE_STRING uAttrName;
        PNDS_RESPONSE_READ_ATTRIBUTE pReadAttrResponse = (PNDS_RESPONSE_READ_ATTRIBUTE) RawResponse;

        RtlInitUnicodeString( &uAttrName, L"Description");

        ntstatus = NwNdsReadAttribute( hTreeConn,
                                       dwOid,
                                       &iterHandle,
                                       &uAttrName,
                                       RawResponse,
                                       sizeof(RawResponse));

        if (  !NT_SUCCESS( ntstatus )
           || ( pReadAttrResponse->CompletionCode != 0 )
           || ( pReadAttrResponse->NumAttributes == 0 )
           )
        {
            // we don't need to set the error since this attribute can only be read by admins and
            // we might get an error indicating this.
            break;
        }

        PNDS_ATTRIBUTE pNdsAttribute = (PNDS_ATTRIBUTE)((DWORD) RawResponse+sizeof(NDS_RESPONSE_READ_ATTRIBUTE));

        LPWSTR pszComment = (LPWSTR) ((DWORD) pNdsAttribute + 3*sizeof(DWORD)
                                      + pNdsAttribute->AttribNameLength + sizeof(DWORD));
        ::SetDlgItemText(hDlg,IDD_NDS_COMMENT, pszComment);

    } while (FALSE);


    if ( hTreeConn )
        CloseHandle( hTreeConn );

    if ( err != NO_ERROR )
    {
        LPWSTR pszMessage = NULL;

        if ( ::LoadMsgErrorPrintf( &pszMessage,
                                   IDS_MESSAGE_GETINFO_ERROR,
                                   err ) == NO_ERROR )
        {
            UnHideControl( hDlg, IDD_ERROR );
            SetDlgItemText( hDlg, IDD_ERROR, pszMessage);
            ::LocalFree( pszMessage );
        }
    }

    return;
}

BOOL
CALLBACK
NDSPage_DlgProc(
    HWND hDlg,
    UINT uMessage,
    WPARAM wParam ,
    LPARAM lParam)
{

    LPPROPSHEETPAGE psp = (LPPROPSHEETPAGE)GetWindowLong(hDlg, DWL_USER);

    switch (uMessage)
    {
        //
        //  When the shell creates a dialog box for a property sheet page,
        // it passes the pointer to the PROPSHEETPAGE data structure as
        // lParam. The dialog procedures of extensions typically store it
        // in the DWL_USER of the dialog box window.
        //
        case WM_INITDIALOG:
            SetWindowLong(hDlg, DWL_USER, lParam);
            psp = (LPPROPSHEETPAGE)lParam;

            NDSPage_InitDialog( hDlg, psp);

            break;

        case WM_DESTROY:
        {
            CNWObjContextMenu*  pPSClass = (CNWObjContextMenu *)(psp->lParam);

            if (pPSClass) 
                pPSClass->Release();

            SetWindowLong(hDlg, DWL_USER, NULL);
            break;
        }

        case WM_COMMAND:
            break;

        case WM_NOTIFY:
        {
            switch (((NMHDR *)lParam)->code) 
            {
                case PSN_SETACTIVE:
                {
                    CNWObjContextMenu *pPSClass = 
                        (CNWObjContextMenu *)(psp->lParam);

                    pPSClass->_paHelpIds = aNDSIds;
                    break;
                }

                default:
                    break;
            }
            break;
        }

        case WM_HELP:
        {
            CNWObjContextMenu*  pPSClass = (CNWObjContextMenu *)(psp->lParam);
        
            if ( pPSClass && pPSClass->_paHelpIds )
            {
                WinHelp( (HWND) ((LPHELPINFO)lParam)->hItemHandle,
                         NW_HELP_FILE,
                         HELP_WM_HELP,
                         (DWORD)(LPVOID)pPSClass->_paHelpIds );
            }
            break;
        }


        case WM_CONTEXTMENU:
        {
            CNWObjContextMenu*  pPSClass = (CNWObjContextMenu*)(psp->lParam);
        
            if (pPSClass && pPSClass->_paHelpIds) 
            {
                WinHelp( (HWND)wParam,
                         NW_HELP_FILE,
                         HELP_CONTEXTMENU,
                         (DWORD)(LPVOID)pPSClass->_paHelpIds );
            }
            break;
        }

        default:
            return FALSE;
    }

    return TRUE;

}

#define HIDWORD(_qw)    (DWORD)(_qw>>32)
#define LODWORD(_qw)    (DWORD)(_qw)

void  Share_InitDialog(HWND hDlg, LPPROPSHEETPAGE psp)
{
    CNWObjContextMenu*  pPSClass = (CNWObjContextMenu *)psp->lParam;
    LPNETRESOURCE       pnr;
    DWORD               err = NO_ERROR;
    BOOL                fDirectoryMap = FALSE;

    if ( pPSClass )
        pnr = pPSClass->QueryNetResource();

    if ( pnr == NULL )
    {
        ASSERT(FALSE);

        // This should not happen. We can always get the net resource which is queried
        // during AddPages.
        return;
    }

    do {  // not a loop, just wanted to break out if error occurred

        WCHAR szShare[MAX_PATH+1];
        WCHAR szServer[MAX_PATH+1] = L"";

        // Get the share name
        NwExtractShareName( pnr->lpRemoteName, szShare );
        SetDlgItemText( hDlg, IDD_SHARE_NAME, szShare );

        HideControl( hDlg, IDD_SHARE_PATH_TXT);
        HideControl( hDlg, IDD_SHARE_PATH);
        HideControl( hDlg, IDD_SHARE_LFN_TXT);

        // Get the server name
        if ( NwIsNdsSyntax( pnr->lpRemoteName ))
        {
            NTSTATUS ntstatus = STATUS_SUCCESS;
            HANDLE hTreeConn = NULL;
            DWORD dwOid;

            err = NwOpenAndGetTreeInfo( pnr->lpRemoteName,
                                        &hTreeConn,
                                        &dwOid );

            if ( err != NO_ERROR )
                break;

            BYTE  RawResponse[TWO_KB];
            DWORD RawResponseSize = sizeof(RawResponse);

            DWORD iterHandle = (DWORD) -1;
            UNICODE_STRING uAttrName;
            PNDS_RESPONSE_READ_ATTRIBUTE pReadAttrResponse = (PNDS_RESPONSE_READ_ATTRIBUTE) RawResponse;

            RtlInitUnicodeString( &uAttrName, L"Path");

            ntstatus = NwNdsReadAttribute( hTreeConn,
                                           dwOid,
                                           &iterHandle,
                                           &uAttrName,
                                           RawResponse,
                                           sizeof(RawResponse));

            CloseHandle( hTreeConn );
            hTreeConn = NULL;

            if (  NT_SUCCESS( ntstatus )
               && ( pReadAttrResponse->CompletionCode == 0 )
               && ( pReadAttrResponse->NumAttributes != 0 )
               )
            {
                // We are successful in reading the attribute. Hence this is a directory map.
                fDirectoryMap = TRUE;

                PNDS_ATTRIBUTE pNdsAttribute = (PNDS_ATTRIBUTE)((DWORD) RawResponse+sizeof(NDS_RESPONSE_READ_ATTRIBUTE));

                PDWORD pdwNameSpace = (PDWORD) ((DWORD) pNdsAttribute + 3*sizeof(DWORD)
                                                + pNdsAttribute->AttribNameLength
                                                + sizeof(WORD)   // need this due to bug in return value???
                                                + sizeof(DWORD));

                // See if the directory supports long file name
                // BUGBUG: what about other name spaces?
                // Only on directory map will Win95 show LFN support or not.
                if ( *pdwNameSpace == NAMESPACE_OS2 )
                {
                    UnHideControl( hDlg, IDD_SHARE_LFN_TXT );
                }

                // Now, try to get the volume the directory map is on
                PDWORD pdwVolumeLen = (PDWORD) ((DWORD) pdwNameSpace + sizeof(DWORD));
                LPWSTR pszVolume = (LPWSTR) ((DWORD) pdwNameSpace + 2*sizeof(DWORD));
                LPWSTR pszPath = (LPWSTR) ((DWORD) pszVolume + *pdwVolumeLen
                                           + sizeof(WORD)    // need this due to bug in return value???
                                           + sizeof(DWORD));


                WCHAR szFullPath[MAX_PATH+1];
                LPWSTR pszTemp;
                wcscpy( szFullPath, pnr->lpRemoteName );
                if ( pszTemp = wcschr( szFullPath + 2, L'\\'))
                    *(pszTemp + 1) = 0;

                wcscat( szFullPath, pszVolume );

                err = NwGetNdsVolumeInfo( szFullPath, szServer, sizeof(szServer), szShare, sizeof(szShare));


                // Now, display the path of the directory map
                if ( err == NO_ERROR )
                {
                    wcscpy( szFullPath, szShare );
                    wcscat( szFullPath, L"\\");
                    wcscat( szFullPath, pszPath );

                    UnHideControl(hDlg, IDD_SHARE_PATH_TXT);
                    UnHideControl(hDlg, IDD_SHARE_PATH);
                    SetDlgItemText( hDlg, IDD_SHARE_PATH, szFullPath );
                }
            }
            else  // this is a volume
            {

                // For NDS names, the unc path might not contain the server name.
                // So, we need to get the server name that this share is on.
                // Also, we need the original volume name like "SYS" instead of "MARS_SRV0_SYS"
                err = NwGetNdsVolumeInfo( pnr->lpRemoteName, szServer, sizeof(szServer), szShare, sizeof(szShare));
            }

            if ( err != NO_ERROR )
                break;
        }
        else  // in the form \\server\sys
        {
            NwExtractServerName( pnr->lpRemoteName, szServer );
        }

        SetDlgItemText( hDlg, IDD_SHARE_SERVER, szServer);

        NWCONN_HANDLE hConn = NULL;
        if ( NWCAttachToFileServerW( szServer, 0, &hConn ) != SUCCESSFUL )
        {
            err = GetLastError();
            break;
        }

        NWVOL_NUM nVolNum;
        char szAnsiShare[MAX_PATH+1];

        ::CharToOem( szShare, szAnsiShare );
        if ( NWCGetVolumeNumber( hConn, szAnsiShare, &nVolNum ) != SUCCESSFUL )
        {
            err = GetLastError();
            break;
        }

        DWORD           dwSectorSize = 0x200;

        DWORD           dwTotalBlocks = 0;
        DWORD           dwFreeBlocks = 0;
        DWORD           dwPurgeable = 0;
        DWORD           dwNotYetPurged = 0;
        DWORD           dwSectors= 0;
        DWORD           dwTotalDir= 0;
        DWORD           dwAvailDir= 0;

        ULONGLONG       qwTot = 0;
        ULONGLONG       qwFree = 0;

        WCHAR           szFormat[30];
        WCHAR           szTemp[80];
        WCHAR           szTemp2[30];


        // NOTE: 2.x servers does not support NWCGetVolumeUsage.
        // Hence, for 2.x servers, an error will always be shown

        if ( NWCGetVolumeUsage( hConn,
                                nVolNum,
                                &dwTotalBlocks,
                                &dwFreeBlocks,
                                &dwPurgeable,
                                &dwNotYetPurged,
                                &dwTotalDir,
                                &dwAvailDir,
                                (LPBYTE) &dwSectors ) != SUCCESSFUL )
        {
            err = GetLastError();
            break;
        }

        dwFreeBlocks += dwPurgeable;

        qwTot =  (ULONGLONG) dwSectorSize * (ULONGLONG) dwSectors * (ULONGLONG) dwTotalBlocks;

        qwFree = (ULONGLONG) dwSectorSize * (ULONGLONG) dwSectors * (ULONGLONG) dwFreeBlocks;

        if (::LoadString(::hmodNW, IDS_BYTES, szFormat, sizeof(szFormat)/sizeof(szFormat[0])))
        {
            if (!HIDWORD(qwTot-qwFree))
            {
                wsprintf(szTemp, szFormat, AddCommas(LODWORD(qwTot) - LODWORD(qwFree), szTemp2, sizeof(szTemp2)/sizeof(szTemp2[0])));
                SetDlgItemText(hDlg,IDD_SHARE_USED_SPC, szTemp);
            }

            if (!HIDWORD(qwFree))
            {
                wsprintf(szTemp, szFormat, AddCommas(LODWORD(qwFree), szTemp2, sizeof(szTemp2)/sizeof(szTemp2[0])));
                SetDlgItemText(hDlg, IDD_SHARE_FREE_SPC, szTemp);
            }

            if (!HIDWORD(qwTot))
            {
                wsprintf(szTemp, szFormat, AddCommas(LODWORD(qwTot), szTemp2, sizeof(szTemp2)/sizeof(szTemp2[0])));
                SetDlgItemText(hDlg, IDD_SHARE_MAX_SPC, szTemp);
            }
        }

        ShortSizeFormat64(qwTot-qwFree, szTemp);
        SetDlgItemText(hDlg, IDD_SHARE_USED_SPC_MB, szTemp);

        ShortSizeFormat64(qwFree, szTemp);
        SetDlgItemText(hDlg, IDD_SHARE_FREE_SPC_MB, szTemp);

        ShortSizeFormat64(qwTot, szTemp);
        SetDlgItemText(hDlg, IDD_SHARE_MAX_SPC_MB, szTemp);

        pPSClass->_fGotClusterInfo = TRUE;
        pPSClass->_dwTotal = dwTotalBlocks;
        pPSClass->_dwFree  = dwFreeBlocks;

        (VOID) NWCDetachFromFileServer( hConn );

    } while (FALSE);

    if ( err != NO_ERROR )
    {
        LPWSTR pszMessage = NULL;

        HideControl(hDlg, IDD_SHARE_USED_SPC_CLR);
        HideControl(hDlg, IDD_SHARE_USED_SPC_TXT);
        HideControl(hDlg, IDD_SHARE_USED_SPC);
        HideControl(hDlg, IDD_SHARE_USED_SPC_MB);

        HideControl(hDlg, IDD_SHARE_FREE_SPC_CLR);
        HideControl(hDlg, IDD_SHARE_FREE_SPC_TXT);
        HideControl(hDlg, IDD_SHARE_FREE_SPC);
        HideControl(hDlg, IDD_SHARE_FREE_SPC_MB);

        HideControl(hDlg, IDD_SHARE_MAX_SPC_TXT);
        HideControl(hDlg, IDD_SHARE_MAX_SPC);
        HideControl(hDlg, IDD_SHARE_MAX_SPC_MB);

        HideControl(hDlg, IDD_SHARE_PIE);

        if ( ::LoadMsgErrorPrintf( &pszMessage,
                                   IDS_MESSAGE_GETINFO_ERROR,
                                   err ) == NO_ERROR )
        {
            UnHideControl( hDlg, IDD_ERROR );
            SetDlgItemText( hDlg, IDD_ERROR, pszMessage);
            ::LocalFree( pszMessage );
        }
    }


} /* endproc Share_InitDialog */

void  Printer_InitDialog(HWND hDlg, LPPROPSHEETPAGE psp)
{
    CNWObjContextMenu*  pPSClass = (CNWObjContextMenu *)psp->lParam;
    LPNETRESOURCE       pnr;
    DWORD               err = NO_ERROR;

    if ( pPSClass )
        pnr = pPSClass->QueryNetResource();

    if ( pnr == NULL )
    {
        ASSERT(FALSE);

        // This should not happen. We can always get the net resource which is queried
        // during AddPages.
        return;
    }

    do {  // not a loop, just wanted to break out if error occurred

        WCHAR szShare[MAX_PATH];
        NwExtractShareName( pnr->lpRemoteName, szShare );


        SetDlgItemText(hDlg,IDD_PRINTER_NAME, szShare);

        if ( NwIsNdsSyntax( pnr->lpRemoteName))
        {
            NTSTATUS ntstatus = STATUS_SUCCESS;
            HANDLE hTreeConn = NULL;
            DWORD dwOid;

            err = NwOpenAndGetTreeInfo( pnr->lpRemoteName,
                                        &hTreeConn,
                                        &dwOid );

            if ( err != NO_ERROR )
                break;

            BYTE  RawResponse[TWO_KB];
            DWORD RawResponseSize = sizeof(RawResponse);

            DWORD iterHandle = (DWORD) -1;
            UNICODE_STRING uAttrName;
            PNDS_RESPONSE_READ_ATTRIBUTE pReadAttrResponse = (PNDS_RESPONSE_READ_ATTRIBUTE) RawResponse;

            RtlInitUnicodeString( &uAttrName, L"Queue Directory");

            ntstatus = NwNdsReadAttribute( hTreeConn,
                                           dwOid,
                                           &iterHandle,
                                           &uAttrName,
                                           RawResponse,
                                           sizeof(RawResponse));

            CloseHandle( hTreeConn );
            hTreeConn = NULL;

            if (  !NT_SUCCESS( ntstatus )
               || ( pReadAttrResponse->CompletionCode != 0 )
               || ( pReadAttrResponse->NumAttributes == 0 )
               )
            {
                // we don't need to set the error since this attribute can only be read by admins and
                // we might get an error indicating this.
                break;
            }

            PNDS_ATTRIBUTE pNdsAttribute = (PNDS_ATTRIBUTE)((DWORD) RawResponse+sizeof(NDS_RESPONSE_READ_ATTRIBUTE));

            LPWSTR pszQueueFile = (LPWSTR) ((DWORD) pNdsAttribute + 3*sizeof(DWORD)
                                            + pNdsAttribute->AttribNameLength + sizeof(DWORD));
            ::SetDlgItemText( hDlg, IDD_PRINTER_QUEUE, pszQueueFile);
        }
        else  // bindery server
        {
            NWCONN_HANDLE hConn = NULL;
            WCHAR szServer[MAX_PATH+1];

            NwExtractServerName( pnr->lpRemoteName, szServer );

            if ( NWCAttachToFileServerW( szServer, 0, &hConn ) != SUCCESSFUL )
                err = GetLastError();

            if ( err == NO_ERROR )
            {
                char szAnsiShare[MAX_PATH+1];
                char Buffer[NW_DATA_SIZE];
                NWFLAGS ucMoreFlag, ucPropertyFlag;

                memset( Buffer, 0, sizeof(Buffer));
                ::CharToOem( szShare, szAnsiShare );

                if ( NWCReadPropertyValue( hConn,
                                           szAnsiShare,
                                           OT_PRINT_QUEUE,
                                           "Q_DIRECTORY",
                                           1,
                                           Buffer,
                                           &ucMoreFlag,
                                           &ucPropertyFlag ) != SUCCESSFUL )
                {
                    err = GetLastError();
                }

                if ( err == NO_ERROR )
                {
                    WCHAR uBuffer[NW_DATA_SIZE];

                    ::OemToChar( Buffer, uBuffer );

                    ::SetDlgItemText( hDlg, IDD_PRINTER_QUEUE, (LPWSTR) uBuffer);
                }
                else
                {
                    err = NO_ERROR;  // Only supervisor has read/write so don't show the error.
                }

                (VOID) NWCDetachFromFileServer( hConn );
            }
        }

    } while (FALSE);

    if ( err != NO_ERROR )
    {
        LPWSTR pszMessage = NULL;

        if ( ::LoadMsgErrorPrintf( &pszMessage,
                                   IDS_MESSAGE_GETINFO_ERROR,
                                   err ) == NO_ERROR )
        {
            UnHideControl( hDlg, IDD_ERROR );
            SetDlgItemText( hDlg, IDD_ERROR, pszMessage);
            ::LocalFree( pszMessage );
        }
    }

} /* endproc Printer_InitDialog */

void  Server_InitDialog(HWND hDlg, LPPROPSHEETPAGE psp)
{
    CNWObjContextMenu*  pPSClass = (CNWObjContextMenu *)psp->lParam;
    LPNETRESOURCE       pnr;
    DWORD               err = NO_ERROR;

    if ( pPSClass )
        pnr = pPSClass->QueryNetResource();

    if ( pnr == NULL )
    {
        ASSERT(FALSE);

        // This should not happen. We can always get the net resource which is queried
        // during AddPages.
        return;
    }

    do {  // not a loop, just wanted to break out if error occurred

        WCHAR szServer[MAX_PATH];
        NwExtractServerName( pnr->lpRemoteName, szServer );

        SetDlgItemText( hDlg, IDD_SERVER_NAME, szServer );

        //
        // Get some server information
        //

        NWCONN_HANDLE hConn = NULL;
        if ( NWCAttachToFileServerW( szServer, 0, &hConn ) != SUCCESSFUL )
        {
            err = GetLastError();
            break;
        }

        VERSION_INFO vInfo;

        if ( NWCGetFileServerVersionInfo( hConn, &vInfo ) != SUCCESSFUL )
        {
            err = GetLastError();
            break;
        }

        WCHAR szTemp[512];
        char  szAnsiCompany[512];
        char  szAnsiVersion[512];
        char  szAnsiRevision[512];

        if ( NWCGetFileServerDescription( hConn, szAnsiCompany, szAnsiVersion,
                                          szAnsiRevision ) != SUCCESSFUL )
        { 
            err = GetLastError();
            break;
        }

        // OemToChar( szAnsiCompany, szTemp );
        // wcscat( szTemp, L" " );
        // OemToChar( szAnsiVersion, szTemp + wcslen( szTemp ));

        OemToChar( szAnsiVersion, szTemp );

        ::SetDlgItemText( hDlg, IDD_SERVER_VERSION, szTemp);

        OemToChar( szAnsiRevision, szTemp );
        ::SetDlgItemText( hDlg, IDD_SERVER_REVISION, szTemp );

        WCHAR szNumber[12];

        ::wsprintf(szNumber,L"%d", vInfo.connsInUse );
        ::SetDlgItemText( hDlg, IDD_SERVER_CONNECT, szNumber);

        ::wsprintf(szNumber,L"%4d", vInfo.ConnsSupported);
        ::SetDlgItemText( hDlg, IDD_SERVER_MAXCON, szNumber);

        (VOID) NWCDetachFromFileServer( hConn );

#if 0
        // Now deal with Chicago specific fields
        if (pPSClass->_fIsPeerServer) {

            pXNCPResp pxresp = (pXNCPResp) pPSClass->_bufServerExInfo.QueryPtr(); ;
            pXGetServerInfoResp lpInfoPtr = (pXGetServerInfoResp)(pxresp+1);
            CHAR    szString[128];  
            STRING  *pNWString;

            // Next field is workgroup name
            pNWString = (STRING *)(lpInfoPtr->passThruServer.str+lpInfoPtr->passThruServer.len);
            pNWString = (STRING *)(pNWString->str+pNWString->len);

            // And next after that is comment

            ::OemToCharBuff((LPCSTR)pNWString->str,szString,pNWString->len);
            szString[pNWString->len] = '\0';

            UnHideControl( hDlg, IDD_SERVER_COMMENT_TXT );
            UnHideControl( hDlg, IDD_SERVER_COMMENT );
            ::SetDlgItemText(hDlg,IDD_SERVER_COMMENT,szString);

        } else
#endif

    } while (FALSE);

    if ( err != NO_ERROR )
    {
        LPWSTR pszMessage = NULL;
 
        if ( ::LoadMsgErrorPrintf( &pszMessage,
                                   IDS_MESSAGE_GETINFO_ERROR,
                                   err ) == NO_ERROR )
        {
            UnHideControl( hDlg, IDD_ERROR );
            SetDlgItemText( hDlg, IDD_ERROR, pszMessage);
            ::LocalFree( pszMessage );
        }
    }

} /* endproc Server_InitDialog */

#if 0
void  Wrkgrp_InitDialog(HWND hDlg, LPPROPSHEETPAGE psp)
{

    CNWObjContextMenu  *pPSClass = (CNWObjContextMenu *)psp->lParam;
    LPNETRESOURCE       pnr;

    if ( pPSClass )
        pnr = (LPNETRESOURCE)pPSClass->_bufNR.QueryPtr();

    if ( pnr )
    {
        // Set name static control
        SetDlgItemText(hDlg,IDD_WRKGRP_NAME, pnr->lpRemoteName);
    }

}
#endif

COLORREF c_crPieColors[] =
{
    RGB(  0,   0, 255),  // Blue
    RGB(255,   0, 255),  // Red-Blue
    RGB(  0,   0, 128),  // 1/2 Blue
    RGB(128,   0, 128),  // 1/2 Red-Blue
} ;

void _DrvPrshtDrawItem(HWND hDlg, LPPROPSHEETPAGE psp, const DRAWITEMSTRUCT * lpdi)
{
    COLORREF crDraw;
    RECT     rcItem = lpdi->rcItem;
    HBRUSH   hbDraw, hbOld;
    SIZE     size;
    HDC      hDC;
    CNWObjContextMenu*    pPSClass = (CNWObjContextMenu *)psp->lParam;

    if (pPSClass->_fGotClusterInfo == FALSE)
        return;

    switch (lpdi->CtlID)
    {
    case IDD_SHARE_PIE:

        hDC = GetDC(hDlg);
        GetTextExtentPoint(hDC, L"W", 1, &size);
        ReleaseDC(hDlg, hDC);

        DrawPie(lpdi->hDC, &lpdi->rcItem,
            pPSClass->_dwTotal ? 1000*(pPSClass->_dwTotal-pPSClass->_dwFree)/pPSClass->_dwTotal : 1000,
            pPSClass->_dwFree==0 || pPSClass->_dwFree==pPSClass->_dwTotal,
            size.cy*2/3, c_crPieColors);

        break;

    case IDD_SHARE_USED_SPC_CLR:
        crDraw = c_crPieColors[DP_USEDCOLOR];
        goto DrawColor;

    case IDD_SHARE_FREE_SPC_CLR:
        crDraw = c_crPieColors[DP_FREECOLOR];
        goto DrawColor;

DrawColor:
        hbDraw = CreateSolidBrush(crDraw);
        if (hbDraw)
        {
            hbOld = (HBRUSH) SelectObject(lpdi->hDC, hbDraw);
            if (hbOld)
            {
                PatBlt(lpdi->hDC, rcItem.left, rcItem.top,
                    rcItem.right-rcItem.left,
                    rcItem.bottom-rcItem.top,
                    PATCOPY);

                SelectObject(lpdi->hDC, hbOld);
            }

            DeleteObject(hbDraw);
        }
        break;

    default:
        break;
    }
}


BOOL CALLBACK NWPage_DlgProc(HWND hDlg, UINT uMessage, WPARAM wParam , LPARAM lParam)
{
   LPPROPSHEETPAGE psp = (LPPROPSHEETPAGE)GetWindowLong(hDlg, DWL_USER);

   switch (uMessage)
   {
      //
      //  When the shell creates a dialog box for a property sheet page,
      // it passes the pointer to the PROPSHEETPAGE data structure as
      // lParam. The dialog procedures of extensions typically store it
      // in the DWL_USER of the dialog box window.
      //
      case WM_INITDIALOG:
         SetWindowLong(hDlg, DWL_USER, lParam);
         psp = (LPPROPSHEETPAGE)lParam;

         if (psp->pszTemplate == MAKEINTRESOURCE(DLG_SERVER_SUMMARYINFO))
             Server_InitDialog(hDlg, psp);
         else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_SHARE_SUMMARYINFO))
             Share_InitDialog(hDlg, psp);
         else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_PRINTER_SUMMARYINFO))
             Printer_InitDialog(hDlg, psp);
#if 0
         else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_WRKGRP_SUMMARYINFO))
             Wrkgrp_InitDialog(hDlg, psp);
#endif

         break;

      case WM_DRAWITEM:
         _DrvPrshtDrawItem(hDlg, psp, (DRAWITEMSTRUCT *)lParam);
         break;

      case WM_DESTROY:
         {
            CNWObjContextMenu*  pPSClass = (CNWObjContextMenu *)(psp->lParam);

            if (pPSClass) {
                pPSClass->Release();
            }

            SetWindowLong(hDlg, DWL_USER, NULL);
         }
         break;

      case WM_COMMAND:
         break;

      case WM_NOTIFY:
         switch (((NMHDR *)lParam)->code) {
             case PSN_SETACTIVE:
             {
                 CNWObjContextMenu *pPSClass = (CNWObjContextMenu *)(psp->lParam);

                 if (psp->pszTemplate == MAKEINTRESOURCE(DLG_SERVER_SUMMARYINFO))
                     pPSClass->_paHelpIds = aServerIds;
                 else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_SHARE_SUMMARYINFO))
                     pPSClass->_paHelpIds = aShareIds;
                 else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_PRINTER_SUMMARYINFO))
                     pPSClass->_paHelpIds = aPrinterIds;
#if 0
                 else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_WRKGRP_SUMMARYINFO))
                     pPSClass->_paHelpIds = aWGIds;
#endif

                 break;
             }

             default:
                 break;
         }

         break;

      case WM_HELP:
        {
            CNWObjContextMenu*  pPSClass = (CNWObjContextMenu *)(psp->lParam);
        
            if (pPSClass && pPSClass->_paHelpIds)
            {
                WinHelp( (HWND) ((LPHELPINFO)lParam)->hItemHandle,
                         NW_HELP_FILE,
                         HELP_WM_HELP,
                        (DWORD)(LPVOID)pPSClass->_paHelpIds );
            }
        }

        break;

      case WM_CONTEXTMENU:
        {
            CNWObjContextMenu*  pPSClass = (CNWObjContextMenu*)(psp->lParam);
        
            if (pPSClass && pPSClass->_paHelpIds)
            {
                WinHelp( (HWND)wParam,
                         NW_HELP_FILE,
                         HELP_CONTEXTMENU,
                         (DWORD)(LPVOID)pPSClass->_paHelpIds );
            }
            break;
        }

      default:
        return(FALSE);
   }

   return(TRUE);

}

// Regular StrToInt; stops at first non-digit.
int WINAPI MyStrToInt(LPWSTR lpSrc) // atoi()
{

#define ISDIGIT(c)  ((c) >= '0' && (c) <= '9')

    int n = 0;
    BOOL bNeg = FALSE;

    if (*lpSrc == L'-') {
        bNeg = TRUE;
    lpSrc++;
    }

    while (ISDIGIT(*lpSrc)) {
    n *= 10;
    n += *lpSrc - L'0';
    lpSrc++;
    }
    return bNeg ? -n : n;
}

// The following functions are stolen from win\core\shell\shelldll
// takes a DWORD add commas etc to it and puts the result in the buffer
LPWSTR WINAPI AddCommas( DWORD dw, LPWSTR pszResult, DWORD dwSize )
{
    WCHAR  szTemp[30];
    WCHAR  szSep[5];
    NUMBERFMT nfmt;

    nfmt.NumDigits=0;
    nfmt.LeadingZero=0;
    GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szSep, sizeof(szSep)/sizeof(szSep[0]));
    nfmt.Grouping = MyStrToInt(szSep);
    GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szSep, sizeof(szSep)/sizeof(szSep[0]));
    nfmt.lpDecimalSep = nfmt.lpThousandSep = szSep;
    nfmt.NegativeOrder= 0;

#pragma data_seg(".text", "CODE")
    wsprintf(szTemp, L"%lu", dw);
#pragma data_seg()

    if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, szTemp, &nfmt, pszResult, dwSize) == 0)
        lstrcpy(pszResult, szTemp);

    return pszResult;
}

const short pwOrders[] = {IDS_BYTES, IDS_ORDERKB, IDS_ORDERMB, IDS_ORDERGB, IDS_ORDERTB};

LPWSTR WINAPI ShortSizeFormat64(ULONGLONG dw64, LPWSTR szBuf)
{
    int i;
    UINT wInt, wLen, wDec;
    WCHAR szTemp[10], szOrder[20], szFormat[5];

    if (dw64 < 1000) {
#pragma data_seg(".text", "CODE")
        wsprintf(szTemp, L"%d", LODWORD(dw64));
#pragma data_seg()
        i = 0;
        goto AddOrder;
    }

    for (i = 1; i< sizeof(pwOrders)/sizeof(pwOrders[0])-1 && dw64 >= 1000L * 1024L; dw64 >>= 10, i++);
        /* do nothing */

    wInt = LODWORD(dw64 >> 10);
    AddCommas(wInt, szTemp, sizeof(szTemp)/sizeof(szTemp[0]));
    wLen = lstrlen(szTemp);
    if (wLen < 3)
    {
        wDec = LODWORD(dw64 - (ULONGLONG)wInt * 1024L) * 1000 / 1024;
        // At this point, wDec should be between 0 and 1000
        // we want get the top one (or two) digits.
        wDec /= 10;
        if (wLen == 2)
            wDec /= 10;

        // Note that we need to set the format before getting the
        // intl char.
#pragma data_seg(".text", "CODE")
        lstrcpy(szFormat, L"%02d");
#pragma data_seg()

        szFormat[2] = L'0' + 3 - wLen;
        GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
                szTemp+wLen, sizeof(szTemp)/sizeof(szTemp[0])-wLen);
        wLen = lstrlen(szTemp);
        wLen += wsprintf(szTemp+wLen, szFormat, wDec);
    }

AddOrder:
    ::LoadString(::hmodNW, pwOrders[i], szOrder, sizeof(szOrder)/sizeof(szOrder[0]));
    wsprintf(szBuf, szOrder, (LPSTR)szTemp);

    return szBuf;
}