/*++

    Copyright (c) 1994  Microsoft Corporation

Module Name:

    FindClnt.C

Abstract:

    Displays the Searching for clients dialog box and looks for the selected
    client distribution tree returning the path and type of path found.

Author:

    Bob Watson (a-robw)

Revision History:

    24 Jun 94    Written

--*/
//
//  Windows Include Files
//

#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include <tchar.h>      // unicode macros
#include <lmcons.h>     // lanman API constants
#include <lmerr.h>      // lanman error returns
#include <lmshare.h>    // sharing API prototypes
#include <lmapibuf.h>   // lanman buffer API prototypes
//
//  app include files
//
#include "otnboot.h"
#include "otnbtdlg.h"
//
// local dialog box messages
//
#define     NCDU_SEARCH_FOR_CLIENTS       (WM_USER+101)
//
//      Search Phases
#define     SEARCH_REGISTRY         (0x00000001)
#define     SEARCH_SHARED_DIRS      (0x00000002)
#define     SEARCH_HARD_DRIVES      (0x00000004)
#define     SEARCH_CD_ROM           (0x00000008)
#define     SEARCH_LAST_PHASE       SEARCH_CD_ROM
//
//  module static variables
//
static BOOL     bSearchForClients;  // flag to trip out of search
static HCURSOR  hOrigCursor = NULL; // cursor to save/restore

LONG
GetDistributionPath (
    IN  HWND        hwndDlg,        // handle to dialog box window
    IN  DWORD       dwSearchType,   // type of dir to find: Client/tools
    IN  OUT LPTSTR  szPath,         // buffer to return path in (Req'd)
    IN  DWORD       dwPathLen,      // size of path buffer in chars
    IN  PLONG       plPathType      // pointer to buffer recieving path type (opt)
)
/*++

Routine Description:
        
    Gets the default distribution file path for loading the dialog box
        entries with.

Arguments:

    IN  HWND        hwndDlg
        handle to parent dialog box window
    IN  DWORD       dwSearchType
        type of dir to find: Client/tools
    IN  OUT LPTSTR  szPath
        buffer to return path in (Req'd)
    IN  DWORD       dwPathLen
        size of path buffer in chars
    IN  PLONG       plPathType
        pointer to buffer recieving path type (opt)

Return Value:

    ERROR_SUCCESS if file found
    ERROR_FILE_NOT_FOUND if unable to find file

--*/
{
    FDT_DATA    Fdt;
    UINT        nDBReturn;

    // build data structure for search

    Fdt.szPathBuffer = szPath;
    Fdt.dwPathBufferLen = dwPathLen;
    Fdt.plPathType = plPathType;
    Fdt.dwSearchType = dwSearchType;

    nDBReturn = DialogBoxParam (
        (HINSTANCE)GetWindowLong(hwndDlg, GWL_HINSTANCE),
        MAKEINTRESOURCE(NCDU_FINDING_CLIENT_DIRS_DLG),
        hwndDlg,
        FindClientsDlgProc,
        (LPARAM)&Fdt);

    if (nDBReturn == IDOK) {
        return ERROR_SUCCESS;
    } else {
        return ERROR_FILE_NOT_FOUND;
    }
}

static
LPCTSTR
GetLastPathFromRegistry (
    IN  DWORD   dwSearchType
)
/*++

Routine Description:
    
    looks up the last path (server\share) for either the tools directory
        or the client tree as it appears in the registry. If unable to
        find both components of the selected search, then an empty string
        is returned.

Arguments:

    IN  DWORD   dwSearchType
        FDT_TOOLS_TREE  for server tools path
        FDT_CLIENT_TREE for client distribution path

Return Value:

    pointer to a read only string that contains the desired path if
        one was stored in the registry or an empty string if not.

--*/
{
    static  TCHAR    szLastPath[MAX_PATH];
    HKEY    hkeyUserInfo;
    HKEY    hkeyAppInfo;

    LONG    lStatus;
    DWORD   dwBufLen;

    // open registry key containing net apps

    lStatus = RegOpenKeyEx (
        HKEY_CURRENT_USER,
        cszUserInfoKey,
        0L,
        KEY_READ,
        &hkeyUserInfo);

    if (lStatus != ERROR_SUCCESS) {
        // unable to open key so return an empty buffer
        szLastPath[0] = 0;
    } else {
        // open registry key containing this app's info
        lStatus = RegOpenKeyEx (
            hkeyUserInfo,
            szAppName,
            0L,
            KEY_READ,
            &hkeyAppInfo);

        if (lStatus != ERROR_SUCCESS) {
            // unable to open key so return an empty buffer
            szLastPath[0] = 0;
        } else {
            // initialize path variable
            lstrcpy (szLastPath, cszDoubleBackslash);

            // get server name from registry
            dwBufLen = MAX_COMPUTERNAME_LENGTH + 1;
            lStatus = RegQueryValueEx (
                hkeyAppInfo,
                (LPTSTR)(dwSearchType == FDT_TOOLS_TREE ? cszLastToolsServer : cszLastClientServer),
                (LPDWORD)NULL,
                (LPDWORD)NULL,
                (LPBYTE)&szLastPath[lstrlen(szLastPath)],
                &dwBufLen);

            if (lStatus != ERROR_SUCCESS) {
                // unable to read value so return an empty buffer
                szLastPath[0] = 0;
            } else {
                // get sharepoint name from registry
                lstrcat (szLastPath, cszBackslash);
                dwBufLen = MAX_SHARENAME + 1;
                lStatus = RegQueryValueEx (
                    hkeyAppInfo,
                    (LPTSTR)(dwSearchType == FDT_TOOLS_TREE ? cszLastToolsSharepoint : cszLastClientSharepoint),
                    (LPDWORD)NULL,
                    (LPDWORD)NULL,
                    (LPBYTE)&szLastPath[lstrlen(szLastPath)],
                    &dwBufLen);

                if (lStatus != ERROR_SUCCESS) {
                    // unable to read value so return an empty buffer
                    szLastPath[0] = 0;
                }
            }
            RegCloseKey (hkeyAppInfo);
        }
        RegCloseKey (hkeyUserInfo);
    }

    return (LPCTSTR)&szLastPath[0];
}

static
LONG
SearchForDistPath (
    IN  OUT LPTSTR  szPath,         // buffer to return path in (Req'd)
    IN  DWORD       dwPathLen,      // size of path buffer in chars
    IN  PLONG       plPathType,     // pointer to buffer recieving path type (opt)
    IN  DWORD       dwSearchType,   // type of tree to look for
    IN  DWORD       dwPhase         // phase(s) to search
)
/*++

Routine Description:
    
    function that looks for the desired directory tree in the following
        locations:
            a) the registry, for the stored server\share
            b) the shared directories on the system
            c) the local and redirected drives
            d) the CD-Rom

        The search is divided into phases to allow the user to cancel the
        search.

Arguments:

    IN  OUT LPTSTR  szPath,         // buffer to return path in (Req'd)
    IN  DWORD       dwPathLen,      // size of path buffer in chars
    IN  PLONG       plPathType,     // pointer to buffer recieving path type (opt)
    IN  DWORD       dwSearchType,   // type of tree to look for
    IN  DWORD       dwPhase         // phase(s) to search

Return Value:

    ERROR_SUCCESS    

--*/
{
    LONG    lStatus = ERROR_SUCCESS;
    LONG    lPathType = NCDU_NO_CLIENT_PATH_FOUND;
    LPTSTR  szLocalPath = NULL;
    NET_API_STATUS  naStatus = NERR_Success;
    DWORD   dwTotalEntries;
    DWORD   dwEntriesRead;
    DWORD   dwEntriesProcessed;
    DWORD   dwResumeHandle;
    DWORD   dwIndex;
    DWORD   dwBufLen;
    BOOL    bFound;
    UINT    nDriveType;
    TCHAR   szRootDir[32];
    PSHARE_INFO_2    psi2Data;
    UINT    nErrorMode;
    BOOL    bValidPath;

    if (szPath == NULL) {
        // the pointer to the path is required
        return ERROR_INVALID_PARAMETER;
    }

    // allocate temp buffers

    szLocalPath = GlobalAlloc (GPTR, MAX_PATH_BYTES);

    if (szLocalPath == NULL) {
        return ERROR_OUTOFMEMORY;
    }

    // disable windows error message popup
    nErrorMode = SetErrorMode  (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);

    bFound = FALSE;

    if ((dwPhase & SEARCH_REGISTRY) == SEARCH_REGISTRY) {
        // check registry for saved server/sharepoint

        // if server/share found in registry, make into a UNC path
        //  and validate that it's really a client tree. return path if valid

        lstrcpy (szLocalPath, GetLastPathFromRegistry(dwSearchType));
        if (szLocalPath[lstrlen(szLocalPath)] != cBackslash) lstrcat (szLocalPath, cszBackslash);
        if (dwSearchType == FDT_TOOLS_TREE){
            bValidPath = (BOOL)(ValidSrvToolsPath (szLocalPath) == 0);
        } else {
            bValidPath = (BOOL)(ValidSharePath (szLocalPath) == 0);
         }
        if (bValidPath) {
            // it's there so save it in user's buffer and leave
            lstrcpy (szPath, szLocalPath);
            lPathType = NCDU_PATH_FROM_REGISTRY;
            lStatus = ERROR_SUCCESS;
            goto GDP_ExitPoint;
        }
    }

    if ((dwPhase & SEARCH_SHARED_DIRS) == SEARCH_SHARED_DIRS)  {
        // if here, then no valid server/share was found in registry, so
        // look at shared dir's on this machine and see if any of them are
        // valid client trees. If one is found, return the path in UNC form

        // search all current shares on this system

        dwEntriesProcessed = 0;
        dwEntriesRead = 0;
        dwTotalEntries = 0;
        dwResumeHandle = 0;
        while ((naStatus = NetShareEnum(
            NULL,
            2,
            (LPBYTE *)&psi2Data,
            0x00010000,
            &dwEntriesRead,
            &dwTotalEntries,
            &dwResumeHandle)) == NERR_Success) {
            if (dwEntriesRead == 0) break;  // then it'd done
            for (dwIndex = 0; dwIndex < dwEntriesRead; dwIndex++){
                // don't check shares that translate to floppy drives A: & B:
                if ((_tcsnicmp(psi2Data[dwIndex].shi2_path, cszADriveRoot, 3) != 0) &&
                    (_tcsnicmp(psi2Data[dwIndex].shi2_path, cszBDriveRoot, 3) != 0)) {
                    if (dwSearchType == FDT_TOOLS_TREE){
                        bValidPath = (BOOL)(ValidSrvToolsPath (psi2Data[dwIndex].shi2_path) == 0);
                    } else {
                        bValidPath = (BOOL)(ValidSharePath (psi2Data[dwIndex].shi2_path) == 0);
                    }
                    if (bValidPath) {
                        // make a UNC name out of share name
                        lstrcpy (szLocalPath, cszDoubleBackslash);
                        dwBufLen = MAX_COMPUTERNAME_LENGTH+1;
                        GetComputerName (&szLocalPath[2],  &dwBufLen);
                        lstrcat (szLocalPath, cszBackslash);
                        lstrcat (szLocalPath, psi2Data[dwIndex].shi2_netname);
                        lstrcat (szLocalPath, cszBackslash);

                        if (lstrlen(szLocalPath) < (LONG)dwPathLen) {
                            // save path string in user's buffer and leave
                            lstrcpy (szPath, szLocalPath);
                            lPathType = NCDU_LOCAL_SHARE_PATH;
                            lStatus = ERROR_SUCCESS;
                            bFound = TRUE;
                        }
                        break;
                    }
                }
            }
            // free buffer created by Net API
            if (psi2Data != NULL) NetApiBufferFree (psi2Data);
            // update entry counters to know when to stop looping
            dwEntriesProcessed += dwEntriesRead;
            if ((dwEntriesProcessed >= dwTotalEntries) || bFound) {
                break; // out of while loop
            }
        }
        if (bFound) goto GDP_ExitPoint;
    }

    if ((dwPhase & SEARCH_HARD_DRIVES) == SEARCH_HARD_DRIVES) {
        // if here, then no shared path was found, so search hard drives for
        // a client tree in the root directory and return the DOS path if one
        // is found

        szRootDir[0] = 0;
        szRootDir[1] = cColon;
        szRootDir[2] = cBackslash;
        szRootDir[3] = 0;
        for (szRootDir[0] = cC; szRootDir[0] <= cZ; szRootDir[0]++) {
            // if it's local or remote drive look for a clients dir.
            // don't check CD_ROM, RAM Disks or Removable drive
            nDriveType = GetDriveType(szRootDir);
            if ((nDriveType == DRIVE_FIXED) || (nDriveType == DRIVE_REMOTE)) {
                // see if this is drive has the appropriate sub-dir on it
                if (dwSearchType == FDT_TOOLS_TREE){
                    lstrcpy (&szRootDir[3], cszToolsDir);
                    bValidPath = (BOOL)(ValidSrvToolsPath (szRootDir) == 0);
                } else {
                    lstrcpy (&szRootDir[3], cszClientsDir);
                    bValidPath = (BOOL)(ValidSharePath (szRootDir) == 0);
                }
                if (bValidPath) {
                    // a valid path was found
                    if (nDriveType == DRIVE_REMOTE) {
                        // then this drive is shared on another machine
                        // so return the UNC version of the path
                        dwBufLen = MAX_PATH * sizeof(TCHAR);
                        if (LookupRemotePath (szRootDir, szLocalPath, &dwBufLen)) {
                            // save path string in user's buffer and leave
                            lstrcpy (szPath, szLocalPath);
                            lPathType = NCDU_LOCAL_SHARE_PATH;
                            lStatus = ERROR_SUCCESS;
                        } else {
                            // unable to look up redirected drive so return dos
                            // version of path (this shouldn't happen);
                            lstrcpy (szPath, szRootDir);
                            lPathType = NCDU_HARD_DRIVE_PATH;
                            lStatus = ERROR_SUCCESS;
                        }
                    } else {
                        // this is a Local drive so return the DOS
                        // version of path
                        lstrcpy (szPath, szRootDir);
                        lPathType = NCDU_HARD_DRIVE_PATH;
                        lStatus = ERROR_SUCCESS;
                    }
                    bFound = TRUE;
                    break;
                }
            } // else ignore if not a local or remote hard drive
            szRootDir[3] = 0;   // reset string back to a drive only
        } // end of for loop
        if (bFound) goto GDP_ExitPoint;
    }

    if ((dwPhase & SEARCH_CD_ROM) == SEARCH_CD_ROM) {
        // if here, then no client tree was found on a hard drive, so see if
        // they have a CD-ROM with the client tree on it. If they do, then
        // return the DOS path of the dir.

        // find CD-ROM drive
        szRootDir[0] = 0;
        szRootDir[1] = cColon;
        szRootDir[2] = cBackslash;
        szRootDir[3] = 0;

        for (szRootDir[0] = cC; szRootDir[0] <= cZ; szRootDir[0]++) {
            if (GetDriveType(szRootDir) == DRIVE_CDROM) break;
        }

        if (szRootDir[0] <= cZ) {
            // then a CD-ROM must have been found, so append the "clients" dir
            // and see if this is a valid client tree
            if (dwSearchType == FDT_TOOLS_TREE){
                lstrcat (szRootDir, cszToolsDir);
                bValidPath = (BOOL)(ValidSrvToolsPath (szRootDir) == 0);
            } else {
                lstrcat (szRootDir, cszClientsDir);
                bValidPath = (BOOL)(ValidSharePath (szRootDir) == 0);
            }
            if (bValidPath) {
                // found one on the CD so return the DOS
                // version of path
                lstrcpy (szPath, szRootDir);
                lPathType = NCDU_CDROM_PATH;
                lStatus = ERROR_SUCCESS;
                bFound = TRUE;
            }
        }
        goto GDP_ExitPoint;
    }

    // if here, then NO client tree was found. so return an empty string
    // bufffer and error code.

    lStatus = ERROR_SUCCESS;
    lPathType = NCDU_NO_CLIENT_PATH_FOUND;
    *szPath = 0;   // make string buffer empty

GDP_ExitPoint:

    if (plPathType != NULL) {
        *plPathType = lPathType;
    }

    FREE_IF_ALLOC (szLocalPath);

    SetErrorMode (nErrorMode);  // restore old error mode

    return lStatus;
}

static
BOOL
FindClientsDlg_WM_INITDIALOG (
    IN  HWND    hwndDlg,
    IN  WPARAM  wParam,
    IN  LPARAM  lParam
)
/*++

Routine Description:
    
    Dialog Box initialization routine:
        calls routines that format the currently selected options
        for display in the static text fields of the dialog box

Arguments:

    IN  HWND    hwndDlg
        Handle to dialog box window

    IN  WPARAM  wParam
        Not Used

    IN  LPARAM  lParam
        pointer to client search data strucutre

Return Value:
    
    FALSE  because focus is set in this routine to the CANCEL button

--*/
{
    PFDT_DATA   pFdt = (PFDT_DATA)lParam;
    LPTSTR      szTitle;

    // locate windw
    PositionWindow  (hwndDlg);

    // set global flag
    bSearchForClients = TRUE;

    // clear dialog box text
    SetDlgItemText (hwndDlg, NCDU_CLIENT_SEARCH_PHASE, cszEmptyString);
    
    // display Tools Tree search string if appropriate
    szTitle = GlobalAlloc (GPTR, MAX_PATH_BYTES);
    if (szTitle != NULL) {
        if (pFdt->dwSearchType == FDT_TOOLS_TREE) {
            if (LoadString (
                (HINSTANCE)GetWindowLong(hwndDlg, GWL_HINSTANCE),
                NCDU_FINDING_TOOLS_PATH,
                szTitle,
                80) > 0) {
                SetDlgItemText (hwndDlg, NCDU_SEARCH_TYPE_TITLE, szTitle);
            }
        }
        FREE_IF_ALLOC (szTitle);
    }

    // start 1st phase of search
    PostMessage (hwndDlg, NCDU_SEARCH_FOR_CLIENTS, (WPARAM)SEARCH_REGISTRY, lParam);
    
    // set focus
    SetFocus (GetDlgItem(hwndDlg, IDCANCEL));

    // need an arrow cursor to cancel out of dialog box.

    hOrigCursor = SetCursor (LoadCursor(NULL, IDC_ARROW));

    return FALSE;
}

static
BOOL
FindClientsDlg_SEARCH_FOR_CLIENTS (
    IN  HWND    hwndDlg,    // dlg window handle
    IN  WPARAM  wParam,     // search phase
    IN  LPARAM  lParam      // search data structure
)
/*++

Routine Description:
    
    message processing routine to perform client tree search in phases    

Arguments:

    IN  HWND    hwndDlg
        dlg window handle

    IN  WPARAM  wParam
        search phase

    IN  LPARAM  lParam
        search data structure

Return Value:

    TRUE    

--*/
{
    UINT        nPhaseName;
    PFDT_DATA   pFdt;

    if (bSearchForClients) {
        // perform this phase of the search
        // set dlg box text
        switch (wParam) {
            case SEARCH_REGISTRY:
                nPhaseName = CSZ_SYSTEM_REGISTRY;
                break;

            case SEARCH_SHARED_DIRS:
                nPhaseName = CSZ_SHARED_DIRS;
                break;

            case SEARCH_HARD_DRIVES:
                nPhaseName = CSZ_HARD_DISK_DIRS;
                break;

            case SEARCH_CD_ROM:
                nPhaseName = CSZ_CD_ROM;
                break;

            default:
                nPhaseName = CSZ_LOCAL_MACHINE;
                break;
        }
        SetDlgItemText (hwndDlg, NCDU_CLIENT_SEARCH_PHASE,
            GetStringResource (nPhaseName));

        pFdt = (PFDT_DATA)lParam;

        // search for clients

        SearchForDistPath (
            pFdt->szPathBuffer,
            pFdt->dwPathBufferLen,
            pFdt->plPathType,
            pFdt->dwSearchType,
            (DWORD)wParam);

        if (*pFdt->plPathType != NCDU_NO_CLIENT_PATH_FOUND) {
            // client found, so end here
            EndDialog (hwndDlg, IDOK);
        } else {
            // try next phase
            if (wParam != SEARCH_LAST_PHASE) {
                wParam <<= 1;  // go to next phase
                PostMessage (hwndDlg, NCDU_SEARCH_FOR_CLIENTS,
                    wParam, lParam);
            } else {
                // this is the last phase so exit
                EndDialog (hwndDlg, (bSearchForClients ? IDOK : IDCANCEL));
            }
        }
    }
    return TRUE;
}

static
BOOL
FindClientsDlg_WM_COMMAND (
    IN  HWND    hwndDlg,
    IN  WPARAM  wParam,
    IN  LPARAM  lParam
)
/*++

Routine Description:
    
    WM_COMMAND message dispatching routine.
        Dispatches IDCANCEL and IDOK button messages, sends all others
        to the DefDlgProc.

Arguments:

    IN  HWND    hwndDlg
        Handle to dialog box window

    IN  WPARAM  wParam
        windows message wParam arg

    IN  LPARAM  lParam
        windows message lParam arg

Return Value:

    TRUE if message is not dispatched (i.e. not processed)
        othewise the value returned by the called routine.

--*/
{

    switch (LOWORD(wParam)) {
        case IDCANCEL:
            switch (HIWORD(wParam)) {
                case BN_CLICKED:
                    bSearchForClients = FALSE;
                    return TRUE;

                default:
                    return FALSE;
            }

        default:
            return FALSE;
    }
}
static
BOOL
FindClientsDlg_WM_DESTROY (
    IN  HWND    hwndDlg,
    IN  WPARAM  wParam,
    IN  LPARAM  lParam
)
/*++

Routine Description:
    
    restores original cursor when dialog box exits

Arguments:

    std. windows message args

Return Value:

    TRUE    

--*/
{
    if (hOrigCursor != NULL) SetCursor (hOrigCursor);
    hOrigCursor = NULL;
    return TRUE;
}

static
BOOL CALLBACK
FindClientsDlgProc (
    IN  HWND    hwndDlg,
    IN  UINT    message,
    IN  WPARAM  wParam,
    IN  LPARAM  lParam
)
/*++

Routine Description:
    
    main dialog proc for this dialog box.
        Processes the following messages:

            WM_INITDIALOG:              dialog box initialization
            WM_COMMAND:                 command button/item selected
            WM_DESTROY:                 restore cursor on exit
            NCDU_SEARCH_FOR_CLIENTS:    execute search message

Arguments:

    IN  HWND    hwndDlg
        handle to dialog box window

    IN  UINT    message
        message id

    IN  WPARAM  wParam
        message wParam arg

    IN  LPARAM  lParam
        message lParam arg

Return Value:

    FALSE if message not processed by this module, otherwise the
        value returned by the message processing routine.

--*/
{
    switch (message) {
        // windows messages
        case WM_INITDIALOG: return (FindClientsDlg_WM_INITDIALOG (hwndDlg, wParam, lParam));
        case WM_COMMAND:    return (FindClientsDlg_WM_COMMAND (hwndDlg, wParam, lParam));
        case WM_DESTROY:    return (FindClientsDlg_WM_DESTROY (hwndDlg, wParam, lParam));
        // local messages
        case NCDU_SEARCH_FOR_CLIENTS:    return (FindClientsDlg_SEARCH_FOR_CLIENTS (hwndDlg, wParam, lParam));
        default:            return FALSE;
    }
}