/* CPPDOC_BEGIN_EXCLUDE  */
/***************************************************************************

  OxYD Socket v0.1

  File : OxYDSocket.cpp


  Versions :
     - v0.1 (20/01/2003) : Initial release

 ---------------------------------------------------------------------------

  Copyright (C) 2003  Grégory WILMES -- Engage Security (http://www.engagesecurity.com)
  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 ***************************************************************************/
/* CPPDOC_END_EXCLUDE  */

#include "stdafx.h"
#include "OxYDSocket.h"

BOOL CALLBACK GetWindowProcX(HWND hWnd, LPARAM lParam);
BOOL CALLBACK GetWindowProc(HWND hWnd, LPARAM lParam);
//HWND GetWindow();
HWND GetChildWindow(HWND hWnd, CString strCmp);

char szTitle[256];
stat winds[1000];
unsigned int nCount = 0;
HWND m_hWndTmp = 0;
HANDLE hProcess;


/**
 * The constructor. Initializes all variables to their default value.
 */
COxYDSocket::COxYDSocket()
{
    m_hWnd = 0;
    m_hWndUrl = 0;
    m_pWnd = 0;
    m_pWndUrl = 0;

    m_nLastError = -1;
    m_bBrowserRunning = FALSE;

    m_strRemoteAddress.Empty();
    m_strRemotePort.Empty();
    m_strWindowOldText.Empty();
}

/**
 * The destructor. Shutdown OxYD Socket.
 */
COxYDSocket::~COxYDSocket()
{
    ShutDown();
}


/**
 * Check is the browser is already running.
 *
 * @return BOOL : Browser state (TRUE = Up, FALSE = Down)
 */
BOOL COxYDSocket::IsBrowserRunning()
{
    return m_bBrowserRunning;
}

/**
 * Initializes OxYD Socket.
 *
 * @param strRemoteAddress CString : Remote address
 * @param strRemotePort CString : Remote port (default = 80)
 * @return BOOL : Connecting state (TRUE = Ok, FALSE = Error)
 */
BOOL COxYDSocket::Connect(CString strRemoteAddress, CString strRemotePort)
{
    if(m_hWnd == 0)
    {
        char szBuffer[512], szBufferCLSID[512];
        long nLen = sizeof(szBuffer), nLenCLSID = sizeof(szBuffer);
        int nPos, nCount = 0;
        CString strRunningDir;

        STARTUPINFO stInfo;
        PROCESS_INFORMATION prInfo;
        BOOL bResult;

        // Find browser path
        //  %HOMEDRIVE%%HOMEPATH%
        //  ( ex : "C:\Program Files\Internet Explorer\IEXPLORE.EXE" )
        if(RegQueryValue(HKEY_CLASSES_ROOT, "htmlfile\\CLSID", szBufferCLSID, &nLenCLSID) != ERROR_SUCCESS)
        {
            m_nLastError = ERROR__UNABLE_TO_QUERY_VALUE_FROM_REGISTRY;
            return FALSE;
        }

        sprintf(szBuffer, "CLSID\\%s\\DefaultIcon", szBufferCLSID);
        if(RegQueryValue(HKEY_CLASSES_ROOT, szBuffer, szBuffer, &nLen) != ERROR_SUCCESS)
        {
            m_nLastError = ERROR__UNABLE_TO_QUERY_VALUE_FROM_REGISTRY;
            return FALSE;
        }

        strRunningDir = szBuffer;

        if( (nPos = strRunningDir.Find(',')) == -1 )
        {
            m_nLastError = ERROR__UNABLE_TO_FIND_BROWSER_RUNNING_DIRECTORY;
            return FALSE;
        }

        strRunningDir = strRunningDir.Left(nPos);

        // Check if Internet Explorer is the default browser
        if(strRunningDir.Right(strlen(BROWSER_EXE)) != BROWSER_EXE)
        {
            m_nLastError = ERROR__WRONG_DEFAULT_BROWSER;
            return FALSE;
        }

        // Launch browser (create process)
        ZeroMemory(&stInfo, sizeof(stInfo));
        stInfo.cb = sizeof(stInfo);
        stInfo.dwFlags = STARTF_USESHOWWINDOW;
#ifdef _DEBUG
        stInfo.wShowWindow = SW_SHOW;
#else
        stInfo.wShowWindow = SW_HIDE;
#endif

        bResult = CreateProcess(NULL, 
                                (LPSTR)(LPCSTR)strRunningDir,
                                NULL, 
                                NULL, 
                                TRUE,
                                CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS,
                                NULL,
                                NULL,
                                &stInfo,
                                &prInfo);

        if(bResult == FALSE)
        {
            m_nLastError = ERROR__UNABLE_TO_CREATE_A_NEW_PROCESS;
            return FALSE;
        }

        hProcess = prInfo.hProcess;

        // Try to find process (browser) window
        m_hWndTmp = 0;

        while(m_hWndTmp == 0)
        {
            if(EnumWindows((WNDENUMPROC)GetWindowProcX, (LPARAM)prInfo.dwProcessId) == 0)
            {
                // Failed to enumerates all top-level windows
                m_nLastError = ERROR__FAILED_TO_ENUMERATES_ALL_TOPLEVEL_WINDOWS;
                return FALSE;
            }

            if(m_hWndTmp == 0)
            {
                if(nCount++ > 200)  // 50ms * 200 = 10s
                {
                    TerminateProcess(hProcess, 0);

                    m_nLastError = ERROR__FAILED_TO_FIND_BROWSER_WINDOW;
                    return FALSE;
                }

                Sleep(50);
            }

        }

        m_hWnd = m_hWndTmp;
    }

    if(m_hWnd != 0)
    {
        RECT Rect;

        m_strRemoteAddress = strRemoteAddress;
        m_strRemotePort = strRemotePort;

        // Get browser window handle
        m_pWnd = CWnd::FromHandle(m_hWnd);

        // Hide browser window
        m_pWnd->GetWindowRect(&m_Rect);
        memcpy(&Rect, &m_Rect, sizeof(RECT));
        
#ifndef _DEBUG
        Rect.left -= 5000;
        Rect.right -= 5000;
        m_pWnd->MoveWindow(&Rect, TRUE);
#endif

        // Get current window title
        m_pWnd->GetWindowText(m_strWindowOldText);

        // Get Url (edit) window handle
        if(m_hWndUrl = GetChildWindow(m_hWnd, "Edit"))
        {
            m_pWndUrl = CWnd::FromHandle(m_hWndUrl);

            // Set tool window style (invisible in taskbar)
#ifndef _DEBUG
            m_pWnd->ModifyStyleEx(0, WS_EX_TOOLWINDOW, 0);
#endif

            m_bBrowserRunning = TRUE;
        }
        else
        {
            m_nLastError = ERROR__FAILED_TO_ENUMERATES_ALL_TOPLEVEL_WINDOWS;
            m_bBrowserRunning = FALSE;
        }
    }
    else
        m_bBrowserRunning = FALSE;

    return m_bBrowserRunning;
}

/**
 * Initializes OxYD Socket.
 *
 * @param strRemoteAddress CString : Remote address
 * @param nRemotePort unsigned short : Remote port (default = 80)
 * @return BOOL : Connecting state (TRUE = Ok, FALSE = Error)
 */
BOOL COxYDSocket::Connect(CString strRemoteAddress, unsigned short nRemotePort)
{
    CString strRemotePort;

    strRemotePort.Format("%u");

    return Connect(strRemoteAddress, strRemotePort);
}

/**
 * Shutdown OxYD Socket if still running.
 *
 * @return BOOL : Shutdown state (TRUE = Ok, FALSE = Error)
 */
BOOL COxYDSocket::ShutDown()
{
    if(IsBrowserRunning())
    {
        // Hide browser window
#ifndef _DEBUG
        m_pWnd->ShowWindow(SW_HIDE);

        /*// Remove tool window style (visible in taskbar)
        m_pWnd->ModifyStyleEx(WS_EX_TOOLWINDOW, 0, 0);

        // Show browser window
        m_pWnd->ShowWindow(SW_SHOW);*/
#endif

        m_pWnd->MoveWindow(&m_Rect, TRUE);

        //PostMessage(m_hWnd, WM_CLOSE, 0, 0);  // Soft method to close the browser
        if(TerminateProcess(hProcess, 0) == 0)
        {
            m_nLastError = ERROR__UNABLE_TO_TERMINATE_THE_PROCESS;
            return FALSE;
        }

        m_hWnd = m_hWndTmp = 0;
        m_bBrowserRunning = FALSE;
        
        return TRUE;
    }

    return FALSE;
}

/**
 * Send a packet with OxYD Socket.
 *
 * @param strTxt CString : String to send
 * @return BOOL : Sending state (TRUE = Ok, FALSE = Error)
 */
BOOL COxYDSocket::SendRaw(CString strTxt)
{
    if(IsBrowserRunning())
    {
        //DataToHex((LPSTR)(LPCSTR)strTxt, strlen(strTxt), strTxt);

        // Set Url text
        //m_pWndUrl->SetWindowText((LPCTSTR)(m_strRemoteAddress + "/" + strTxt));  // Nothing happened with this method...
        if( m_strRemotePort.IsEmpty() || (m_strRemotePort == "80") )
            m_pWndUrl->SendMessage(WM_SETTEXT, 0, (LPARAM)(LPCTSTR)("http://" + m_strRemoteAddress + "/" + strTxt));
        else
            m_pWndUrl->SendMessage(WM_SETTEXT, 0, (LPARAM)(LPCTSTR)("http://" + m_strRemoteAddress + ":" + m_strRemotePort + "/" + strTxt));

        // Simulate 'Enter' key (VK_ENTER = 13)
        m_pWndUrl->SendMessage(WM_KEYDOWN, 13, 0);
        m_pWndUrl->SendMessage(WM_CHAR, 13, 0);
        m_pWndUrl->SendMessage(WM_KEYUP, 13, 0);

        return TRUE;
    }

    m_nLastError = ERROR__THE_BROWSER_IS_NOT_RUNNING;
    return FALSE;
}

/**
 * Receive a packet with OxYD Socket.
 *
 * @param strTxt &CString : String to receive
 * @return BOOL : Receiving state (TRUE = Ok, FALSE = Error)
 */
BOOL COxYDSocket::ReceiveRaw(CString &strTxt)
{
    if(IsBrowserRunning())
    {
        CString strWindowText;
        const int nLen = strlen(BROWSER);

        // Get browser window text
        do {
            Sleep(50);
            m_pWnd->GetWindowText(strWindowText);
        } while(strWindowText == m_strWindowOldText);

        if(strWindowText.Right(nLen) == BROWSER)
            strWindowText = strWindowText.Left(strlen(strWindowText) -nLen);
        else
        {
            m_nLastError = ERROR__THE_BROWSER_IS_NOT_RUNNING;
            return FALSE;
        }

        //HexToData(strWindowText, (LPSTR)(LPCSTR)strWindowText);
        strTxt = m_strWindowOldText = strWindowText;

        return TRUE;
    }

    m_nLastError = ERROR__THE_BROWSER_IS_NOT_RUNNING;
    return FALSE;
}


/**
 * Return remote address.
 *
 * @return CString : Remote address
 */
CString COxYDSocket::GetRemoteAddress()
{
    return m_strRemoteAddress;
}

/**
 * Return remote port.
 *
 * @return CString : Remote port
 */
CString COxYDSocket::GetRemotePort()
{
    return m_strRemotePort;
}

/**
 * Get last error number.
 *
 * @return int : Last error number
 */
int COxYDSocket::GetLastError()
{
    return m_nLastError;
}

/**
 * Get last error string from error number.
 *
 * @return CString : Last error (string format)
 */
CString COxYDSocket::FormatMessage(int nLastError)
{
    CString strLastError;

    if(nLastError == -1)
        nLastError = m_nLastError;

    if(nLastError == ERROR__UNKNOWN_ERROR)
        strLastError = "Unknown error";
//  else if(nLastError == ERROR__UNABLE_TO_CREATE_VALUE_KEY)
//      strLastError = "Unable to create value key";
    else if(nLastError == ERROR__UNABLE_TO_QUERY_VALUE_FROM_REGISTRY)
        strLastError = "Unable to query value from registry";
    else if(nLastError == ERROR__UNABLE_TO_FIND_BROWSER_RUNNING_DIRECTORY)
        strLastError = "Unable to find browser running directory";
    else if(nLastError == ERROR__WRONG_DEFAULT_BROWSER)
        strLastError = "Wrong default browser";
    else if(nLastError == ERROR__UNABLE_TO_CREATE_A_NEW_PROCESS)
        strLastError = "Unable to create a new process";
    else if(nLastError == ERROR__FAILED_TO_ENUMERATES_ALL_TOPLEVEL_WINDOWS)
        strLastError = "Failed to enumerates all top-level windows";
    else if(nLastError == ERROR__FAILED_TO_FIND_BROWSER_WINDOW)
        strLastError = "Failed to find browser window";
    else if(nLastError == ERROR__UNABLE_TO_TERMINATE_THE_PROCESS)
        strLastError = "Unable to terminate the process";
    else if(nLastError == ERROR__THE_BROWSER_IS_NOT_RUNNING)
        strLastError = "The browser is not running";
    else
        strLastError = "Unknown error code !";

    return strLastError;
}


/**
 * GetWindowProcX
 *
 * @param hWnd 
 * @param lParam 
 * @return BOOL 
 */
BOOL CALLBACK GetWindowProcX(HWND hWnd, LPARAM lParam)
{
    DWORD dwID;

    GetWindowThreadProcessId(hWnd, &dwID);

    if(dwID == (DWORD)lParam)
    {
        CString strWindowText;

        GetWindowText(hWnd, szTitle, sizeof(szTitle) -1);
        strWindowText = szTitle;

        if(strWindowText.Right(strlen(BROWSER)) == BROWSER)
            m_hWndTmp = hWnd;
    }

    return TRUE;
}

/**
 * GetWindowProc
 *
 * @param hWnd 
 * @param lParam 
 * @return BOOL 
 */
BOOL CALLBACK GetWindowProc(HWND hWnd, LPARAM lParam)
{
    GetWindowText(hWnd, szTitle, sizeof(szTitle) -1);

    winds[nCount].hWnd = hWnd;
//  winds[nCount].strText = szTitle;

    nCount++;

    return TRUE;
}

/**
 * GetChildWindow
 *
 * @param hWnd 
 * @param strCmp 
 * @return HWND 
 */
HWND GetChildWindow(HWND hWnd, CString strCmp)
{
    char szClassName[1024];

    nCount = 0;
    
    if(EnumChildWindows(hWnd, (WNDENUMPROC)GetWindowProc, 0) == 0)
    {
        // Failed to enumerates all top-level windows
        return 0;
    }
    while(EnumChildWindows(winds[nCount -1].hWnd, (WNDENUMPROC)GetWindowProc, 0) != 0);
    
    for(unsigned int i = 0; i < nCount; i++)
    {
        if(GetClassName(winds[i].hWnd, szClassName, sizeof(szClassName) -1))
        {
            if(strcmp(szClassName, strCmp) == 0)
                return winds[i].hWnd;
        }
    }

    return 0;
}