/****************************************************************************\

    MISCAPI.C / OPK Wizard (OPKWIZ.EXE)

    Microsoft Confidential
    Copyright (c) Microsoft Corporation 1999
    All rights reserved

    Misc. API source file for generic APIs used in the OPK Wizard.

    4/99 - Jason Cohen (JCOHEN)
        Added this new source file for the OPK Wizard as part of the
        Millennium rewrite.
        
    09/2000 - Stephen Lodwick (STELO)
        Ported OPK Wizard to Whistler

\****************************************************************************/


//
// Include file(s)
//
#include "pch.h"
#include "resource.h"


//
// Internal Defined Value(s):
//

#define STR_URLDEF          _T("http://")
#define STR_EVENT_CANCEL    _T("SETUPMGR_EVENT_CANCEL")


//
// Internal Defined Macro(s):
//

#define MALLOC(cb)          HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb)
#define FREE(lp)            ( (lp != NULL) ? ( (HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, (LPVOID) lp)) ? ((lp = NULL) == NULL) : (FALSE) ) : (FALSE) )


//
// Internal Type Definition(s):
//

typedef struct _COPYDIRDATA
{
    HWND    hwndParent;
    LPTSTR  lpSrc;
    LPTSTR  lpDst;
    HANDLE  hEvent;
} COPYDIRDATA, *PCOPYDIRDATA, *LPCOPYDIRDATA;


//
// Internal Function Prototype(s):
//

LRESULT CALLBACK CopyDirDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
DWORD WINAPI CopyDirThread(LPVOID lpVoid);


//
// External Function(s):
//

// If we find a key name with _Gray then *pfGray == TRUE
//
void ReadInstallInsKey(TCHAR szSection[], TCHAR szKey[], TCHAR szValue[], INT cchValue, TCHAR szIniFile[], BOOL* pfGray)
{
    TCHAR szTempKey[MAX_PATH];
    HRESULT hrCat;

    if (!pfGray)
        return;

    lstrcpyn(szTempKey, szKey, AS(szTempKey));
    if (!OpkGetPrivateProfileString(szSection, szTempKey, szValue, szValue, cchValue, szIniFile)) {
        hrCat=StringCchCat(szTempKey, AS(szTempKey), GRAY);
        if (OpkGetPrivateProfileString(szSection, szTempKey, szValue, szValue, cchValue, szIniFile))
                *pfGray = TRUE;
        else
            *pfGray = TRUE; // default to unchecked if not found!
    }
    else
        *pfGray = FALSE;
}

// If pfGrayed == TRUE then concatenate _Gray to the key name
//
void WriteInstallInsKey(TCHAR szSection[], TCHAR szKey[], TCHAR szValue[], TCHAR szIniFile[], BOOL fGrayed)
{
    TCHAR szKeyTemp[MAX_PATH];
    HRESULT hrCat;

    // Clear the old value
    //
    lstrcpyn(szKeyTemp, szKey, AS(szKeyTemp));
    OpkWritePrivateProfileString(szSection, szKeyTemp, NULL, szIniFile);
    hrCat=StringCchCat(szKeyTemp, AS(szKeyTemp), GRAY);
    OpkWritePrivateProfileString(szSection, szKeyTemp, NULL, szIniFile);

    // Write the new value
    lstrcpyn(szKeyTemp, szKey, AS(szKeyTemp));
    if (fGrayed) 
        hrCat=StringCchCat(szKeyTemp, AS(szKeyTemp), GRAY);
    OpkWritePrivateProfileString(szSection, szKeyTemp, szValue, szIniFile);
}

//  NOTE: pszFileName must point to buffer at least length MAX_PATH
void CheckValidBrowseFolder(TCHAR* pszFileName)
{
    if (NULL == pszFileName)
        return;

    // Last known good browse start folder
    //
    PathRemoveFileSpec(pszFileName);
    if (!lstrlen(pszFileName))
        lstrcpyn(pszFileName, g_App.szLastKnownBrowseFolder, MAX_PATH);
}

void SetLastKnownBrowseFolder(TCHAR* pszFileName)
{
    if (NULL == pszFileName)
        return;

    // Save Last known good browse start folder
    //
    PathCombine(g_App.szLastKnownBrowseFolder, pszFileName, NULL);
    PathRemoveFileSpec(g_App.szLastKnownBrowseFolder);
}

// NOTE: lpszURL is assumed to point to a buffer at least MAX_URL in length
BOOL ValidURL(LPTSTR lpszURL)
{
    BOOL    bResult             = TRUE;
    TCHAR   szBuffer[MAX_PATH]  = NULLSTR;
    HRESULT hrCat;

    // Check if valid URL
    //
    if ( !PathIsURL(lpszURL) )
    {
        // Check if empty string first
        //
        if (0 == lstrlen(lpszURL))
            bResult = FALSE;
        else {
            // Currently not a valid URL, we are now going to prepend the
            // URL with http:// and then test the validity again
            //
            lstrcpyn(szBuffer, STR_URLDEF, AS(szBuffer));
            hrCat=StringCchCat(szBuffer, AS(szBuffer), lpszURL);
        
            // Still not a valid URL or we were unable to copy the string
            //
            if ( !PathIsURL(szBuffer) ||
                 !lstrcpyn(lpszURL, szBuffer, MAX_URL) )
                bResult = FALSE;
        }
    }

    return bResult;

}

BOOL IsFolderShared(LPWSTR lpFolder, LPWSTR lpShare, DWORD cbShare)
{
    LPSHARE_INFO_502        lpsi502 = NULL;
    DWORD                   dwRead  = 0,
                            dwTotal = 0;
    NET_API_STATUS          nas;
    BOOL                    bRet    = FALSE,
                            bBest   = FALSE,
                            bBuffer = ( lpShare && cbShare );
    PACL                    paclOld = NULL;
    TCHAR                   szUnc[MAX_COMPUTERNAME_LENGTH + 4] = NULLSTR;

    // Success or failure, we will always atleast pass back the computer
    // name if they passed in a buffer.  So here is where we create the
    // computer name part of the path.
    //
    if ( bBuffer )
    {
        DWORD cbUnc = AS(szUnc) - 2;
        HRESULT hrCat;

        // We want to return the UNC path, so first need the \\ plus the
        // computer name.
        //
        // NOTE:  We hard coded the length of the "\\" string below as 2
        //        in two different places.  Once just above, and once below
        //        in the GetComputerName() call.  We also hard code the "\"
        //        string below as 1 when adding to the lenght of the string
        //        after adding the computer name.  So don't forget these things
        //        if you make some changes here.
        //
        lstrcpyn(szUnc, _T("\\\\"), cbUnc);
        if ( ( GetComputerName(szUnc + 2, &cbUnc) ) &&
             ( AS(szUnc) > ((DWORD) lstrlen(szUnc) + 1) ) )
        {
            // Added on a backslash so we can add the share name.
            //
            hrCat=StringCchCat(szUnc,AS(szUnc), _T("\\"));
        }
        else
        {
            // If GetComputerName() fails, that is bad.  But we will just
            // return the share name.  That is about all we can do.
            //
            szUnc[0] = NULLCHR;
        }

    }
 
    // Now share time, first retrieve all the shares on this machine.
    //
    nas = NetShareEnum(NULL, 502, (unsigned char **) &lpsi502, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal, NULL);

    // Make sure we got a list of shares, otherwise there is nothing.
    // we can do.  Because we specify MAX_PREFERRED_LENGTH, we should
    // never get ERROR_MORE_DATA, but if for some reason we do there is
    // no reason not to loop through the ones we did get.
    //
    if ( ( lpsi502 ) &&
         ( ( nas == NERR_Success ) || ( nas == ERROR_MORE_DATA ) ) )
    {
        int     iLength     = lstrlen(lpFolder);
        LPTSTR  lpSearch    = lpFolder + iLength;
        HRESULT hrCat;

        // Trailing backslash is only bad if not the root folder.
        //
        if ( iLength > 3 )
        {
            // See if the folder has a trailing backslash.
            //
            lpSearch = CharPrev(lpFolder, lpSearch);
            if ( *lpSearch == _T('\\') )
            {
                iLength--;
            }
        }

        // Go through all the shares until we fine the best
        // one for this directory.
        //
        while ( dwRead-- && !bBest )
        {
            // See if this share is a disk share and is the
            // same path passed in.
            //
            if ( ( lpsi502[dwRead].shi502_type == STYPE_DISKTREE ) &&
                 ( StrCmpNI(lpsi502[dwRead].shi502_path, lpFolder, iLength) == 0 ) &&
                 ( lstrlen(lpsi502[dwRead].shi502_path) == iLength ) )
            {
                // If this directory is shared more than once, we want to use
                // the first one we fine with no security descriptor, because 
                // then it is most likely shared out to everyone.
                //
                if ( lpsi502[dwRead].shi502_security_descriptor == NULL )
                {
                    // If there is no security descriptor, then everyone should have
                    // access and this is a good share.
                    //
                    bBest = TRUE;
                }

                // If we have no ACL, or we reset it because the new one is better,
                // then we want to copy off the share name into our return buffer.
                //
                if ( !bRet || bBest )
                {
                    // Return the share name for this directory in the supplied buffer
                    // (if the buffer is NULL or zero size, we just return TRUE so they
                    // know that the folder is shared even if they don't care what the
                    // name of the share is).
                    //
                    if ( bBuffer )
                    {
                        // Find out what we have room for in the return buffer.
                        //
                        if ( cbShare > (DWORD) (lstrlen(lpsi502[dwRead].shi502_netname) + lstrlen(szUnc)) )
                        {
                            // Copy the computer name and share into return buffer.
                            //
                            lstrcpyn(lpShare, szUnc, cbShare);
                            hrCat=StringCchCat(lpShare, cbShare, lpsi502[dwRead].shi502_netname);
                        }
                        else if ( cbShare > (DWORD) lstrlen(lpsi502[dwRead].shi502_netname) )
                        {
                            // Return buffer not big enough for both computer name and
                            // share name, so just return the share name.
                            //
                            lstrcpyn(lpShare, lpsi502[dwRead].shi502_netname,cbShare);
                        }
                        else
                        {
                            // Not big enough for both, so return TRUE because we found a
                            // share, but don't return anything in the buffer.
                            //
                            *lpShare = NULLCHR;
                        }
                    }
                }

                // We found one, so always set this to TRUE.
                //
                bRet = TRUE;
            }
        }
    }

    // Make sure and free the buffer returned by NetShareEnum().
    //
    if ( lpsi502 )
        NetApiBufferFree(lpsi502);

    // Now check to see if we didn't find the share, because we can
    // still just return the computer name.
    //
    if ( ( !bRet && bBuffer ) &&
         ( cbShare > (DWORD) lstrlen(szUnc) ) )
    {
        lstrcpyn(lpShare, szUnc, cbShare);
    }

    return bRet;
}

BOOL CopyDirectoryDialog(HINSTANCE hInstance, HWND hwnd, LPTSTR lpSrc, LPTSTR lpDst)
{
    COPYDIRDATA cdd;

    // Pass in via the structure the source and destination the dialog needs
    // to know about.
    //
    cdd.lpSrc = lpSrc;
    cdd.lpDst = lpDst;

    // Create the progress dialog.
    //
    return ( DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PROGRESS), hwnd, CopyDirDlgProc, (LPARAM) &cdd) != 0 );
}

BOOL CopyResetFileErr(HWND hwnd, LPCTSTR lpSource, LPCTSTR lpTarget)
{
    BOOL bReturn;

    if ( !(bReturn = CopyResetFile(lpSource, lpTarget)) && hwnd )
        MsgBox(hwnd, IDS_MISSINGFILE, IDS_APPNAME, MB_ERRORBOX, lpSource);
    return bReturn;
}


//
// Internal Function(s):
//

LRESULT CALLBACK CopyDirDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static LPCOPYDIRDATA lpcdd = NULL;

    switch (uMsg)
    {
        case WM_INITDIALOG:

            // Make sure we have out copy directory data structure.
            //
            if ( lParam )
            {
                HANDLE  hThread;
                DWORD   dwThreadId;

                // Save off our lParam.
                //
                lpcdd = (LPCOPYDIRDATA) lParam;

                // Replace the old parent with the new progress dialog parent.
                //
                lpcdd->hwndParent = hwnd;

                // Need to pass in the cancel event as well.
                //
                lpcdd->hEvent = CreateEvent(NULL, TRUE, FALSE, STR_EVENT_CANCEL);

                // Now create the thread that will copy the actual files.
                //
                if ( hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) CopyDirThread, (LPVOID) lpcdd, 0, &dwThreadId) )
                    CloseHandle(hThread);
                else
                    EndDialog(hwnd, 0);
            }
            else
                EndDialog(hwnd, 0);

            return FALSE;

        case WM_COMMAND:
        case WM_CLOSE:

            // If we have an event, signal it, or just end the dialog.
            //
            if ( lpcdd && lpcdd->hEvent )
                SetEvent(lpcdd->hEvent);
            else
                EndDialog(hwnd, 0);
            return FALSE;

        case WM_DESTROY:

            // If there is an event, get rid of it.
            //
            if ( lpcdd && lpcdd->hEvent )
            {
                CloseHandle(lpcdd->hEvent);
                lpcdd->hEvent = NULL;
            }
            return FALSE;

        default:
            return FALSE;
    }

    return TRUE;
}

DWORD WINAPI CopyDirThread(LPVOID lpVoid)
{
    LPCOPYDIRDATA   lpcdd           = (LPCOPYDIRDATA) lpVoid;
    HWND            hwnd            = lpcdd->hwndParent,
                    hwndProgress    = GetDlgItem(hwnd, IDC_PROGRESS);
    HANDLE          hEvent          = lpcdd->hEvent;
    DWORD           dwRet           = 0;
    LPTSTR          lpSrc           = lpcdd->lpSrc,
                    lpDst           = lpcdd->lpDst;

    // First we need to create the path.
    //
    if ( CreatePath(lpDst) )
    {
        // Setup the progress bar.
        //
        SendMessage(hwndProgress, PBM_SETSTEP, 1, 0L);
        SendMessage(hwndProgress, PBM_SETRANGE32, 0, (LPARAM) FileCount(lpSrc));

        // Now copy the directory.
        //
        if ( CopyDirectoryProgressCancel(hwndProgress, hEvent, lpSrc, lpDst) )
            dwRet = 1;
    }

    // Now end the dialog with our error code and return.
    //
    EndDialog(hwnd, dwRet);
    return dwRet;
}