/* Copyright (c) 1995, Microsoft Corporation, all rights reserved
**
** util.c
** Remote Access Common Dialog APIs
** Utility routines
** Listed alphabetically
*/

#include "rasdlgp.h"   // Our private header
#include <dlgs.h>      // Common dialog resource constants
#include <lmwksta.h>   // NetWkstaGetInfo
#include <lmapibuf.h>  // NetApiBufferFree


/* Cached workstation and logon information.  See GetLogonUser,
** GetLogonDomain, and GetComputer.
*/
static TCHAR g_szLogonUser[ UNLEN + 1 ];
static TCHAR g_szLogonDomain[ DNLEN + 1 ];
static TCHAR g_szComputer[ CNLEN + 1 ];


/*----------------------------------------------------------------------------
** Help maps
**----------------------------------------------------------------------------
*/

static DWORD g_adwPnHelp[] =
{
    CID_LE_ST_Item,    HID_PN_EB_NewNumber,
    CID_LE_EB_Item,    HID_PN_EB_NewNumber,
    CID_LE_PB_Add,     HID_PN_PB_Add,
    CID_LE_PB_Replace, HID_PN_PB_Replace,
    CID_LE_ST_List,    HID_PN_LB_List,
    CID_LE_LB_List,    HID_PN_LB_List,
    CID_LE_PB_Up,      HID_PN_PB_Up,
    CID_LE_PB_Down,    HID_PN_PB_Down,
    CID_LE_PB_Delete,  HID_PN_PB_Delete,
    CID_LE_CB_Promote, HID_PN_CB_Promote,
    0, 0
};


/*----------------------------------------------------------------------------
** Local helper prototypes (alphabetically)
**----------------------------------------------------------------------------
*/

VOID
GetWkstaUserInfo(
    void );

/*----------------------------------------------------------------------------
** Utility routines (alphabetically)
**----------------------------------------------------------------------------
*/

BOOL
AllLinksAreModems(
    IN PBENTRY* pEntry )

    /* Returns true if all links associated with the entry are modem links
    ** (MXS or Unimodem), false otherwise.
    */
{
    DTLNODE* pNode;

    if (pEntry->pdtllistLinks)
    {
        for (pNode = DtlGetFirstNode( pEntry->pdtllistLinks );
             pNode;
             pNode = DtlGetNextNode( pNode ))
        {
            PBLINK* pLink = (PBLINK* )DtlGetData( pNode );

            if (pLink->pbport.pbdevicetype != PBDT_Modem)
                return FALSE;
        }
    }

    return TRUE;
}


BOOL
AllLinksAreMxsModems(
    IN PBENTRY* pEntry )

    /* Returns true if all links associated with the entry are MXS modem
    ** links, false otherwise.
    */
{
    DTLNODE* pNode;

    if (pEntry->pdtllistLinks)
    {
        for (pNode = DtlGetFirstNode( pEntry->pdtllistLinks );
             pNode;
             pNode = DtlGetNextNode( pNode ))
        {
            PBLINK* pLink = (PBLINK* )DtlGetData( pNode );

            if (pLink->pbport.pbdevicetype != PBDT_Modem
                || !pLink->pbport.fMxsModemPort)
            {
                return FALSE;
            }
        }
    }

    return TRUE;
}


VOID
ContextHelp(
    IN DWORD* padwMap,
    IN HWND   hwndDlg,
    IN UINT   unMsg,
    IN WPARAM wparam,
    IN LPARAM lparam )

    /* Calls WinHelp to popup context sensitive help.  'PadwMap' is an array
    ** of control-ID help-ID pairs terminated with a 0,0 pair.  'UnMsg' is
    ** WM_HELP or WM_CONTEXTMENU indicating the message received requesting
    ** help.  'Wparam' and 'lparam' are the parameters of the message received
    ** requesting help.
    */
{
    HWND hwnd;
    UINT unType;

    ASSERT(unMsg==WM_HELP||unMsg==WM_CONTEXTMENU);

    /* Don't try to do help if it won't work.  See common\uiutil\ui.c.
    */
    {
        extern BOOL g_fNoWinHelp;
        if (g_fNoWinHelp)
            return;
    }

    if (unMsg == WM_HELP)
    {
        LPHELPINFO p = (LPHELPINFO )lparam;;

        TRACE3("ContextHelp(WM_HELP,t=%d,id=%d,h=$%08x)",
            p->iContextType,p->iCtrlId,p->hItemHandle);

        if (p->iContextType != HELPINFO_WINDOW)
            return;

        hwnd = p->hItemHandle;
        ASSERT(hwnd);
        unType = HELP_WM_HELP;
    }
    else
    {
        /* Standard Win95 method that produces a one-item "What's This?" menu
        ** that user must click to get help.
        */
        TRACE1("ContextHelp(WM_CONTEXTMENU,h=$%08x)",wparam);

        hwnd = (HWND )wparam;
        unType = HELP_CONTEXTMENU;
    };

    WinHelp( hwnd, g_pszHelpFile, unType, (DWORD )padwMap );
}


HWND
CreateWizardBitmap(
    IN HWND hwndDlg,
    IN BOOL fPage )

    /* Create a static control that displays the RAS wizard bitmap at the
    ** standard place on dialog 'hwndDlg'.  'FPage' is set if the bitmap is
    ** being placed on a property page, false for the equivalent placement on
    ** a dialog.
    **
    ** Returns the bitmap window handle or NULL or error.
    */
{
    HWND hwnd;
    INT  x;
    INT  y;

    if (fPage)
        x = y = 0;
    else
        x = y = 10;

    hwnd =
        CreateWindowEx(
            0,
            TEXT("static"),
            NULL,
            WS_VISIBLE | WS_CHILD | SS_SUNKEN | SS_BITMAP,
            x, y, 80, 140,
            hwndDlg,
            (HMENU )CID_BM_Wizard,
            g_hinstDll,
            NULL );

    if (hwnd)
    {
        if (!g_hbmWizard)
        {
            g_hbmWizard = LoadBitmap(
                g_hinstDll, MAKEINTRESOURCE( BID_Wizard ) );
        }

        SendMessage( hwnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM )g_hbmWizard );
    }

    return hwnd;
}


TCHAR*
DisplayPszFromDeviceAndPort(
    IN TCHAR* pszDevice,
    IN TCHAR* pszPort )

    /* Returns address of heap block psz containing the MXS modem list display
    ** form, i.e. the device name 'pszDevice' followed by the port name
    ** 'pszPort'.  It's caller's responsibility to Free the returned string.
    */
{
    TCHAR* pszResult;
    TCHAR* pszD;

    if (pszDevice)
        pszD = NULL;
    else
        pszD = pszDevice = PszFromId( g_hinstDll, SID_UnknownDevice );

    pszResult = PszFromDeviceAndPort( pszDevice, pszPort );
    Free0( pszD );

    return pszResult;
}


TCHAR*
FirstPhoneNumberFromEntry(
    IN PBENTRY* pEntry )

    /* Returns the first phone number of the first link of entry 'pEntry' or
    ** an empty string if none.  The returned address is into the list of
    ** phone numbers and should be copied if it needs to be stored.
    */
{
    TCHAR*   pszPhoneNumber;
    DTLNODE* pNode;
    PBLINK*  pLink;

    TRACE("FirstPhoneNumberFromEntry");

    ASSERT(pEntry->pdtllistLinks);
    pNode = DtlGetFirstNode( pEntry->pdtllistLinks );
    ASSERT(pNode);
    pLink = (PBLINK* )DtlGetData( pNode );
    ASSERT(pLink);

    return FirstPszFromList( pLink->pdtllistPhoneNumbers );
}


TCHAR*
FirstPszFromList(
    IN DTLLIST* pPszList )

    /* Returns the first string from the first node of 'pPszList' or an empty
    ** string if none.  The returned address is into the list and should be
    ** copied if it needs to be stored.
    */
{
    TCHAR*   psz;
    DTLNODE* pNode;

    TRACE("FirstPszFromList");

    if (DtlGetNodes( pPszList ) > 0)
    {
        pNode = DtlGetFirstNode( pPszList );
        ASSERT(pNode);
        psz = (TCHAR* )DtlGetData( pNode );
        ASSERT( psz );
    }
    else
        psz = TEXT("");

    return psz;
}


DWORD
FirstPhoneNumberToEntry(
    IN PBENTRY* pEntry,
    IN TCHAR*   pszPhoneNumber )

    /* Sets the first phone number of the first link of entry 'pEntry' to
    ** 'pszPhoneNumber'.
    **
    ** Returns 0 if successful, or an error code.
    */
{
    DTLNODE* pNode;
    PBLINK*  pLink;
    TCHAR*   pszNew;

    TRACE("FirstPhoneNumberToEntry");

    ASSERT(pEntry->pdtllistLinks);
    pNode = DtlGetFirstNode( pEntry->pdtllistLinks );
    ASSERT(pNode);
    pLink = (PBLINK* )DtlGetData( pNode );
    ASSERT(pLink);
    ASSERT(pLink->pdtllistPhoneNumbers);

    return FirstPszToList( pLink->pdtllistPhoneNumbers, pszPhoneNumber );
}


DWORD
FirstPszToList(
    IN DTLLIST* pPszList,
    IN TCHAR*   psz )

    /* Sets the string of the first node of the list 'pPszList' to a copy of
    ** 'psz'.  If 'psz' is "" the first node is deleted.
    **
    ** Returns 0 if successful, or an error code.
    */
{
    DTLNODE* pNode;
    TCHAR*   pszNew;

    ASSERT(pPszList);

    /* Delete the existing first node, if any.
    */
    if (DtlGetNodes( pPszList ) > 0)
    {
        pNode = DtlGetFirstNode( pPszList );
        DtlRemoveNode( pPszList, pNode );
        DestroyPszNode( pNode );
    }

    /* Create a new first node and link it.  An empty string is not added.
    */
    if (*psz == TEXT('\0'))
        return 0;

    pszNew = StrDup( psz );
    pNode = DtlCreateNode( pszNew, 0 );
    if (!pszNew || !pNode)
    {
        Free0( pszNew );
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    DtlAddNodeFirst( pPszList, pNode );
    return 0;
}


TCHAR*
GetComputer(
    void )

    /* Returns the address of a static buffer containing the local
    ** workstation's computer name.
    */
{
    if (g_szComputer[ 0 ] == TEXT('\0'))
    {
        DWORD           dwErr;
        WKSTA_INFO_100* pInfo;

        pInfo = NULL;
        TRACE("NetWkstaGetInfo");
        dwErr = NetWkstaGetInfo( NULL, 100, (LPBYTE* )&pInfo );
        TRACE1("NetWkstaGetInfo=%d",dwErr);

        if (pInfo)
        {
            if (dwErr == 0)
                lstrcpy( g_szComputer, pInfo->wki100_computername );
            NetApiBufferFree( pInfo );
        }
    }

    TRACEW1("GetComputer=%s",g_szComputer);
    return g_szComputer;
}


DWORD
GetDefaultEntryName(
    IN  DTLLIST* pdtllistEntries,
    OUT TCHAR**  ppszName )

    /* Loads a default entry name into '*ppszName' that is unique in the list
    ** of phonebook entries 'pdtllistEntries'.  It is caller's responsibility
    ** to Free the returned string.
    **
    ** Returns 0 if successful or an error code.
    */
{
    TCHAR  szBuf[ RAS_MaxEntryName + 1 ];
    TCHAR* pszDefault;
    DWORD  dwDefaultLen;
    LONG   lNum;

    *ppszName = NULL;

    pszDefault = PszFromId( g_hinstDll, SID_DefaultEntry );
    if (!pszDefault)
        return ERROR_NOT_ENOUGH_MEMORY;

    dwDefaultLen = lstrlen( pszDefault );
    lstrcpy( szBuf, pszDefault );

    lNum = 2;
    while (EntryNodeFromName( pdtllistEntries, szBuf ))
    {
        lstrcpy( szBuf, pszDefault );
        LToT( lNum, szBuf + dwDefaultLen, 10 );
        ++lNum;
    }

    Free( pszDefault );

    *ppszName = StrDup( szBuf );
    if (!*ppszName)
        return ERROR_NOT_ENOUGH_MEMORY;

    return 0;
}


TCHAR*
GetLogonDomain(
    void )
{
    if (g_szLogonDomain[ 0 ] == TEXT('\0'))
        GetWkstaUserInfo();

    TRACEW1("GetLogonDomain=%s",g_szLogonDomain);
    return g_szLogonDomain;
}


TCHAR*
GetLogonUser(
    void )

    /* Returns the address of a static buffer containing the logged on user's
    ** account name.
    */
{
    if (g_szLogonUser[ 0 ] == TEXT('\0'))
        GetWkstaUserInfo();

    TRACEW1("GetLogonUser=%s",g_szLogonUser);
    return g_szLogonUser;
}


VOID
GetWkstaUserInfo(
    void )

    /* Helper to load statics with NetWkstaUserInfo information.  See
    ** GetLogonUser and GetLogonDomain.
    */
{
    DWORD              dwErr;
    WKSTA_USER_INFO_1* pInfo;

    pInfo = NULL;
    TRACE("NetWkstaUserGetInfo");
    dwErr = NetWkstaUserGetInfo( NULL, 1, (LPBYTE* )&pInfo );
    TRACE1("NetWkstaUserGetInfo=%d",dwErr);

    if (pInfo)
    {
        if (dwErr == 0)
        {
            lstrcpy( g_szLogonUser, pInfo->wkui1_username );
            lstrcpy( g_szLogonDomain, pInfo->wkui1_logon_domain );
        }

        NetApiBufferFree( pInfo );
    }
}


BOOL
IsLocalPad(
    IN PBENTRY* pEntry )

    /* Returns true if 'pEntry' is a local PAD device, i.e. the first link of
    ** the entry has device type "pad", false otherwise.
    */
{
    PBLINK*  pLink;
    DTLNODE* pNode;

    if (!pEntry)
        return FALSE;

    ASSERT(pEntry->pdtllistLinks);
    pNode = DtlGetFirstNode( pEntry->pdtllistLinks );
    ASSERT(pNode);
    pLink = (PBLINK* )DtlGetData( pNode );
    ASSERT(pLink);

    return (pLink->pbport.pbdevicetype == PBDT_Pad);
}


VOID
LaunchMonitor(
    IN HWND hwndNotify )

    /* Launch the Dial-Up Networking monitor.  'HwndNotify' is the window to
    ** be reactivated when the monitor is up.
    */
{
    TRACE("LaunchMonitor");

    if (FindWindow( TEXT("RasmonWinClass"), NULL ))
        return;

    /* Write our hwnd in shared memory for rasmon to read.
    */
    if (!g_pRmmem)
    {
        g_hRmmem = CreateFileMapping(
            (HANDLE )0xFFFFFFFF, NULL, PAGE_READWRITE,
            0L, sizeof(RMMEM), RMMEMRASPHONE );
        if (g_hRmmem)
            g_pRmmem = MapViewOfFile( g_hRmmem, FILE_MAP_WRITE, 0L, 0L, 0L );
        else
        {
            TRACE("CreateFileMapping failed");
        }
    }

    if (g_pRmmem)
    {
        g_pRmmem->hwnd = hwndNotify;
        g_pRmmem->pid = GetCurrentProcessId();

        TRACE2("RmMem:h=$%08x,pid=$%08x",g_pRmmem->hwnd,g_pRmmem->pid);
    }

    /* Start RASMON.EXE.
    */
    WinExec( "rasmon.exe", SW_SHOWNA );

    TRACE("LaunchMonitor done");
}


BOOL
PhoneNumberDlg(
    IN     HWND     hwndOwner,
    IN OUT DTLLIST* pList,
    IN OUT BOOL*    pfCheck )

    /* Popup the phone number list dialog.  'HwndOwner' is the owner of the
    ** created dialog.  'PList' is a list of Psz nodes containing the phone
    ** numbers.  'PfCheck' is the address that contains the initial "promote
    ** number" checkbox setting and which receives the value set by user.
    **
    ** Returns true if user presses OK and succeeds, false if he presses
    ** Cancel or encounters an error.
    */
{
    TRACE("PhoneNumberDlg");

    return
        ListEditorDlg(
            hwndOwner,
            pList,
            pfCheck,
            RAS_MaxPhoneNumber,
            PszFromId( g_hinstDll, SID_HuntTitle ),
            PszFromId( g_hinstDll, SID_HuntItemLabel ),
            PszFromId( g_hinstDll, SID_HuntListLabel ),
            PszFromId( g_hinstDll, SID_HuntCheckLabel ),
            NULL,
            0,
            g_adwPnHelp,
            0,
            NULL );
}


VOID
PositionDlg(
    IN HWND hwndDlg,
    IN BOOL fPosition,
    IN LONG xDlg,
    IN LONG yDlg )

    /* Positions the dialog 'hwndDlg' based on caller's API settings, where
    ** 'fPosition' is the RASxxFLAG_PositionDlg flag and 'xDlg' and 'yDlg' are
    ** the coordinates.
    */
{
    if (fPosition)
    {
        /* Move it to caller's coordinates.
        */
        SetWindowPos( hwndDlg, NULL, xDlg, yDlg, 0, 0,
            SWP_NOZORDER + SWP_NOSIZE );
        UnclipWindow( hwndDlg );
    }
    else
    {
        /* Center it on the owner window, or on the screen if none.
        */
        CenterWindow( hwndDlg, GetParent( hwndDlg ) );
    }
}


LRESULT CALLBACK
PositionDlgStdCallWndProc(
    int    code,
    WPARAM wparam,
    LPARAM lparam )

    /* Standard Win32 CallWndProc hook callback that positions the next dialog
    ** to start in this thread at our standard offset relative to owner.
    */
{
    /* Arrive here when any window procedure associated with our thread is
    ** called.
    */
    if (!wparam)
    {
        CWPSTRUCT* p = (CWPSTRUCT* )lparam;

        /* The message is from outside our process.  Look for the MessageBox
        ** dialog initialization message and take that opportunity to position
        ** the dialog at the standard place relative to the calling dialog.
        */
        if (p->message == WM_INITDIALOG)
        {
            RECT rect;
            HWND hwndOwner;

            hwndOwner = GetParent( p->hwnd );
            GetWindowRect( hwndOwner, &rect );
            SetWindowPos( p->hwnd, NULL,
                rect.left + DXSHEET, rect.top + DYSHEET,
                0, 0, SWP_NOZORDER + SWP_NOSIZE );
            UnclipWindow( p->hwnd );
        }
    }

    return 0;
}


TCHAR*
PszFromPhoneNumberList(
    IN DTLLIST* pList )

    /* Returns the phone numbers in phone number list 'pList' in a comma
    ** string or NULL on error.  It is caller's responsiblity to Free the
    ** returned string.
    */
{
    TCHAR*   pszResult;
    DTLNODE* pNode;
    DWORD    cb;

    const TCHAR* pszSeparator = TEXT(", ");

    cb = (DtlGetNodes( pList ) *
             (RAS_MaxPhoneNumber + lstrlen( pszSeparator )) + 1)
             * sizeof(TCHAR);
    pszResult = Malloc( cb );
    if (!pszResult)
        return NULL;

    *pszResult = TEXT('\0');

    for (pNode = DtlGetFirstNode( pList );
         pNode;
         pNode = DtlGetNextNode( pNode ))
    {
        TCHAR* psz = (TCHAR* )DtlGetData( pNode );
        ASSERT(psz);

        if (*pszResult)
            lstrcat( pszResult, pszSeparator );
        lstrcat( pszResult, psz );
    }

    pszResult = Realloc( pszResult,
        (lstrlen( pszResult ) + 1) * sizeof(TCHAR) );
    ASSERT(pszResult);

    return pszResult;
}


#if 0
LRESULT CALLBACK
SelectDesktopCallWndRetProc(
    int    code,
    WPARAM wparam,
    LPARAM lparam )

    /* Standard Win32 CallWndRetProc hook callback that makes "Desktop" the
    ** initial selection of the FileOpen "Look in" combo-box.
    */
{
    /* Arrive here when any window procedure associated with our thread is
    ** called.
    */
    if (!wparam)
    {
        CWPRETSTRUCT* p = (CWPRETSTRUCT* )lparam;

        /* The message is from outside our process.  Look for the MessageBox
        ** dialog initialization message and take that opportunity to set the
        ** "Look in:" combo box to the first item, i.e. "Desktop".  FileOpen
        ** keys off CBN_CLOSEUP rather than CBN_SELCHANGE to update the
        ** "contents" listbox.
        */
        if (p->message == WM_INITDIALOG)
        {
            HWND hwndLbLookIn;

            hwndLbLookIn = GetDlgItem( p->hwnd, cmb2 );
            ComboBox_SetCurSel( hwndLbLookIn, 0 );
            SendMessage( p->hwnd, WM_COMMAND,
                MAKELONG( cmb2, CBN_CLOSEUP ), (LPARAM )hwndLbLookIn );
        }
    }

    return 0;
}
#endif


VOID
TweakTitleBar(
    IN HWND hwndDlg )

    /* Adjust the title bar to include an icon if unowned and the modal frame
    ** if not.  'HwndDlg' is the dialog window.
    */
{
    if (GetParent( hwndDlg ))
    {
        LONG lStyle;
        LONG lStyleAdd;

        /* Drop the system menu and go for the dialog look.
        */
        lStyleAdd = WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE;;

        lStyle = GetWindowLong( hwndDlg, GWL_EXSTYLE );
        if (lStyle)
            SetWindowLong( hwndDlg, GWL_EXSTYLE, lStyle | lStyleAdd );
    }
    else
    {
        /* Stick a rasphone icon in the upper left of the dialog, and
        ** more importantly on the task bar
        */
        SendMessage( hwndDlg, WM_SETICON, TRUE,
            (LONG )LoadIcon( g_hinstDll, MAKEINTRESOURCE( IID_Rasphone ) ) );
    }
}


int CALLBACK
UnHelpCallbackFunc(
    IN HWND   hwndDlg,
    IN UINT   unMsg,
    IN LPARAM lparam )

    /* A standard Win32 commctrl PropSheetProc.  See MSDN documentation.
    **
    ** Returns 0 always.
    */
{
    TRACE2("UnHelpCallbackFunc(m=%d,l=%08x)",unMsg,lparam);

    if (unMsg == PSCB_PRECREATE)
    {
        extern BOOL g_fNoWinHelp;

        /* Turn off context help button if WinHelp won't work.  See
        ** common\uiutil\ui.c.
        */
        if (g_fNoWinHelp)
        {
            DLGTEMPLATE* pDlg = (DLGTEMPLATE* )lparam;
            pDlg->style &= ~(DS_CONTEXTHELP);
        }
    }

    return 0;
}
