/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: prodinstlib.cpp,v 1.2.2.4 2004/07/09 02:03:21 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

/************************************************************************
 * postinst.cpp - Post-install setup program.
 */


/***********************************************************************
 * includes
 */

#include "hxtypes.h"
#include "safestring.h"
#include "hlxclib/ctype.h"

// system includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <time.h>
#include <sys/stat.h>
#include <errno.h>

#ifdef _UNIX
#include <unistd.h>
#include <pwd.h>
#include <grp.h>

#include <termios.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif // _UNIX

#if defined _AIX43
#include <strings.h>
#include "dllpath.h"
ENABLE_MULTILOAD_DLLACCESS_PATHS(Postinst);
#endif // _AIX43

#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#endif // _WIN32

#include <ctype.h>

#include "md5.h"

#ifdef _WIN32
#include "hxstrutl.h"   // strcasecmp
#endif // _WIN32

#include "product_version.h"

#include "package_info.h"
#include "consoleui.h"
#include "prodinstlib.h"
#include "passwdtool.h"

#ifdef _WIN32
#include "wininstlib.h"
#endif // _WIN32


/************************************************************************/
/************************************************************************/


ProductInstaller::ProductInstaller()
    : m_pPorts(NULL) 
    , m_szAdminUser(0)
    , m_szAdminPass(0)
    , m_szAdminRealm(0)
    , m_szEncUser(0)
    , m_szEncPass(0)
    , m_szEncRealm(0)
    , m_szContentRealm(0)
    , m_szMonPass(0)
    , m_szOutputCfg(0)
    , m_szBackupCfg(0)
    , m_szInstallerConfigFile(0)
    , m_szLoggingStyle(0)
    , m_szHostname(0)
    , m_szDestDir(0)
    , m_bNonInteractive(FALSE)
    , m_bSilent(FALSE)
    , m_bBackupOldFiles(TRUE)
    , m_bDebug(FALSE)
    , m_szInstallLog(NULL)
#ifdef _WIN32
    , m_szArchDir(0)
    , m_szServiceUser(NULL)
    , m_szServiceName(NULL)
    , m_szServicePass(NULL)
    , m_bNTService(TRUE)
    , m_bWSAStarted(FALSE)
#endif // _WIN32
    , m_pCUI(0)
{
}

ProductInstaller::~ProductInstaller()
{
    HX_DELETE(m_pCUI);
    HX_VECTOR_DELETE(m_pPorts);
    HX_VECTOR_DELETE(m_szOutputCfg);
    HX_VECTOR_DELETE(m_szBackupCfg);
    HX_VECTOR_DELETE(m_szInstallLog);

#ifdef _WIN32
    if(m_bWSAStarted)
    {
        WSACleanup();
    }
#endif // _WIN32
}

void
ProductInstaller::Init()
{
    if (m_ulNumPorts)
    {
        m_pPorts = new InstPort[m_ulNumPorts];
	if (m_pPorts)
	{
	    for(UINT32 i = 0; i < m_ulNumPorts; i++)
	    {
		m_pPorts[i].nPort = 0;
		m_pPorts[i].bSet = FALSE;
		m_pPorts[i].szName = NULL;
	    }
	    
	    m_pPorts[MON_PORT].szName = "Monitor";
	    m_pPorts[RTSP_PORT].szName = "RTSP";
	    m_pPorts[PNA_PORT].szName = "PNA";
	    m_pPorts[HTTP_PORT].szName = "HTTP";
	    m_pPorts[ADMIN_PORT].szName = "Admin";
	}
    }

#ifdef _CONSOLE
    m_pCUI = new ConsoleUI();
#endif
    
}

/************************************************************************
 * ProductInstaller::SetDefaults()
 */
void
ProductInstaller::SetDefaults(void)
{
    if(GetLocalHostName(m_szHostname) && m_szHostname && 
        m_szHostname[0] != '\0')
    {
        m_szBaseRealm = m_szHostname;
    }
    else
    {
        m_szBaseRealm = (char*)"HelixProduct";
    }
    
    SetDefaultDir(m_szDestDir);

    if (m_pPorts)
    {
	m_pPorts[MON_PORT].nPort = 9090;
	m_pPorts[RTSP_PORT].nPort = 554;
	m_pPorts[PNA_PORT].nPort = 7070;
	m_pPorts[HTTP_PORT].nPort = 80;
	m_pPorts[ADMIN_PORT].nPort = RandomPort(10000, 30000);
	m_pPorts[MON_PORT].bSet = TRUE;
    }

    m_szOutputCfg = new char[strlen(ProductVersion::ExecutableName()) + 5];
    m_szBackupCfg = new char[12];

    sprintf (m_szOutputCfg, "%s.cfg", ProductVersion::ExecutableName());
    sprintf (m_szBackupCfg, "%s.cfg", "default");

    m_szLoggingStyle   = (char*)"5";

    m_szAdminRealm   = new char[strlen(m_szBaseRealm) + 20];
    m_szEncRealm     = new char[strlen(m_szBaseRealm) + 20];
    m_szContentRealm = new char[strlen(m_szBaseRealm) + 20];
    sprintf (m_szAdminRealm,   "%s.AdminRealm",      m_szBaseRealm);
    sprintf (m_szEncRealm,     "%s.EncoderRealm",    m_szBaseRealm);
    sprintf (m_szContentRealm, "%s.ContentRealm",    m_szBaseRealm);

#ifdef _WIN32
    m_bNTService = TRUE;
    m_szServiceUser = NULL;
    m_szServicePass = NULL;
    m_szServiceName = new char[128];
    strcpy (m_szServiceName, ProductVersion::ProductName());
#endif // _WIN32
}


/************************************************************************
 * ProductInstaller::ParseCmdLine - Parse the command-line options
 */
void
ProductInstaller::ParseCmdLine(int argc, const char* argv[])
{
    int i=0;
    const char** arg=0;

    // First, scan all args for -v or -h; if there we want to exit now...
    for (i = argc, arg = argv; i; arg++, i--)
    {
        if (!strcmp(*arg, "--version") ||
            !strcmp(*arg, "-v"))
        {
            if(m_pCUI)
                m_pCUI->ShowMessage(VersionBanner());
            CleanUpAndExit(0, m_pCUI);
        }
        if (!strcmp(*arg, "--help") ||
            !strcmp(*arg, "-h") ||
            !strcmp(*arg, "/?")) // for win32-heads
        {
            if(m_pCUI)
                m_pCUI->PrintUsage("installer", NULL);
            CleanUpAndExit(0, m_pCUI);
        }

        else if (!strcmp(*arg, "--silent") ||
                 !strcmp(*arg, "-s") ||
                 !strcmp(*arg, "/s"))
        {
            m_bSilent = TRUE;
            m_bNonInteractive = TRUE;
        }

    }

    // now scan all args for --out so we can redirect stdout/stderr first thing
    for (i = argc, arg = argv; i; arg++, i--)
    {
        if (arg == argv)
        {
            // this is the command name, so skip it
        }

        else if (!strcmp(*arg, "--progress-only") ||
                 !strcmp(*arg, "-p"))
        {
            if(!m_bSilent && m_pCUI)
                m_pCUI->ShowMessage ("Option: non-interactive install\n");
            m_bNonInteractive = TRUE;
        }

        else if (!strcmp(*arg, "--destination") ||
                 !strcmp(*arg, "--dest") ||
                 !strcmp(*arg, "-dest") || //old installer compatabity cruft
                 !strcmp(*arg, "-d"))
        {
            if (i && *(arg+1)[0] != '-')
            {
                i--;
                arg++;

                if(!m_bSilent && m_pCUI)
                    m_pCUI->ShowMessage ("Option: install to directory %s\n", *arg);
                m_szDestDir = new char[strlen(*arg) + 1];
                strcpy (m_szDestDir, *arg);
            }
            else
            {
                if(!m_bSilent && m_pCUI)
                    m_pCUI->ShowErrorMessage ("Option: %s must take a filename!\n", *arg);
            }

        }

        else if (!strcmp(*arg, "-cfg"))
        {
            if (i && *(arg+1)[0] != '-')
            {
                i--;
                arg++;
                if(!m_bSilent && m_pCUI)
                {
                    m_pCUI->ShowMessage ("Option: read installer configuration "
                             "from %s\n", *arg);
                }
                m_szInstallerConfigFile = new char[strlen(*arg) + 1];
                strcpy (m_szInstallerConfigFile, *arg);
                //
                // Read external installer configuration file, setting 
                // various defaults
                //
                char szCfg[1024];
#ifdef _WIN32
                char* pFile;
                DWORD dwLen = GetFullPathName(*arg, 1024, szCfg, &pFile);
                if(dwLen == 0 || dwLen >= 1024)                
                {
                    sprintf(szCfg, "%s", *arg);
                }
#else // _WIN32
                if (**arg != '/')
                {
                    sprintf(szCfg, "../%s", *arg);
                }
                else
                {
                    sprintf(szCfg, "%s", *arg);
                }
#endif // _WIN32

                if (ReadInstallerDefaults(szCfg) != 0)
                {
                    if(!m_bSilent && m_pCUI)
                        m_pCUI->ShowErrorMessage("ERROR: Unable to read installer config "
                                     "file %s (%s)\n", *arg, strerror(errno));
                    CleanUpAndExit(-1, m_pCUI);
                }
            }
            else
            {
                if(!m_bSilent && m_pCUI)
                    m_pCUI->ShowErrorMessage ("Option: %s must take a filename!\n", *arg);
            }
        }

        else if (!strcmp(*arg, "--no-backup") ||
                 !strcmp(*arg, "--nb") || 
                 !strcmp(*arg, "-nb"))
        {
            if(!m_bSilent && m_pCUI)
                m_pCUI->ShowMessage ("Option: no-backup\n");
            m_bBackupOldFiles = FALSE;
        }

        else if (!strcmp(*arg, "--debug"))
        {
            if(!m_bSilent && m_pCUI)
                m_pCUI->ShowMessage ("Option: debug\n");
            m_bDebug = TRUE;
        }

        else if (!strcmp(*arg, "--password"))
        {
            if (i > 1)
            {
                i--;
                arg++;
            }
        }

        else
        {
            if(!m_bSilent && m_pCUI)
                m_pCUI->ShowErrorMessage  ("ERROR: unknown command-line option: %s\n", *arg);
        }
    }
}

/************************************************************************
 * ProductInstaller::RandomPort - Allocate a random port within the given range.
 */
int
ProductInstaller::RandomPort (int nMin, int nMax)
{
    int nPort;
    int* pUsedPorts = new int[m_ulNumPorts + 1];

    for(UINT32 i = 0; i <= m_ulNumPorts; i++)
    {
        pUsedPorts[i] = m_pPorts[i].nPort; 
    }
    pUsedPorts[m_ulNumPorts] = 0;

    nPort = GenerateRandomPort (nMin, nMax, pUsedPorts);
    delete[] pUsedPorts;

    return nPort;
}

/************************************************************************
 * GenerateRandomPort - Allocate a random port within the given range.
 */
int
ProductInstaller::GenerateRandomPort(int nMin, int nMax, int pUsedPorts[])
{
    int nPort=0;
    int i=0;

    while (1)
    {
        nPort = random() % (nMax - nMin) + nMin;
        if (nPort == 554 ||
            nPort == 1090 ||
            nPort == 1337 ||
            nPort == 1091 ||
            nPort == 3030 ||
            nPort == 4040 ||
            nPort == 5050 ||
            nPort == 6060 ||
            nPort == 7070 ||
            nPort == 8080 ||
            nPort == 7802 ||
            nPort == 7878 ||
            nPort == 31337)
        {
            continue;
        }
        for (i=0; pUsedPorts[i]; ++i)
        {
            if (nPort == pUsedPorts[i])
                continue;
        }

        break;
    }

    return nPort;
}


/************************************************************************
 * ProductInstaller::GenerateRandomPasswords
 *
 * Used for non-interactive install.
 */
void
ProductInstaller::GenerateRandomPasswords(void)
{
    if (!m_szAdminPass)
    {
        m_szAdminPass = PasswordTool::GenerateRandomPassword();
    }
    if (!m_szEncPass)
    {
        m_szEncPass = PasswordTool::GenerateRandomPassword();
    }
    if (!m_szMonPass)
    {
        m_szMonPass = PasswordTool::GenerateRandomPassword();
    }
}


/************************************************************************
 * ProductInstaller::UpdatePasswordDatabase - update the flat-file password 
 * database
 *
 * XXXDC -- our password databases are lame -- FIXME
 */
void
ProductInstaller::UpdatePasswordDatabases()
{
    char szSaveDir[PATH_MAX];
    getcwd(szSaveDir, PATH_MAX);
    if (chdir(m_szDestDir) != 0)
    {
        m_pCUI->ShowErrorMessage("Error accessing directory %s (%s)\n",
            m_szDestDir, strerror(errno));
        return;
    }

    // need to make sure these directories exist
    mkdir("adm_b_db/users", 0755);
    mkdir("adm_b_db/guids", 0755);
    mkdir("adm_b_db/logs", 0755);
    mkdir("adm_b_db/redirect", 0755);

    PasswordTool::AddPasswordFileEntry("adm_b_db", m_szAdminUser,
                                       m_szAdminPass, NULL);

    chdir(szSaveDir);
}

/************************************************************************
 * ProductInstaller::CreateConfigFile - Create a custom config file.
 */
void
ProductInstaller::CreateConfigFile(const char* szOutputCfg)
{
    FILE* fp=0;

    char szSaveDir[PATH_MAX];
    getcwd(szSaveDir, PATH_MAX);
    if (chdir(m_szDestDir) != 0)
    {
        if(!m_bSilent)
        {
            m_pCUI->ShowErrorMessage("Error accessing directory %s (%s)\n",
                m_szDestDir, strerror(errno));
        }
        return;
    }

    char* szConfig = FormatConfigFile();

    fp = fopen(szOutputCfg, "w");
    if (fp)
    {
        fprintf(fp, "%s", szConfig);
        fclose(fp);
    }
    else
    {
        m_pCUI->ShowErrorMessage("Error creating %s (%s)\n", szOutputCfg, 
            strerror(errno));
    }

    chdir(szSaveDir);
    HX_VECTOR_DELETE(szConfig);
}

/************************************************************************
 * ProductInstaller::BackupOldFiles
 */
void
ProductInstaller::BackupOldFiles()
{
    char szSaveDir[PATH_MAX];
    getcwd(szSaveDir, PATH_MAX);
    if (chdir(m_szDestDir) != 0)
    {
        if(!m_bSilent)
        {
            m_pCUI->ShowErrorMessage("Error accessing directory %s (%s)\n",
                m_szDestDir, strerror(errno));
        }
        return;
    }

    BackupIfPresent("Bin");
    BackupIfPresent("Plugins");
    BackupIfPresent("Lib");
    BackupIfPresent(m_szOutputCfg);
    BackupIfPresent(m_szBackupCfg);

    chdir(szSaveDir);
}


/************************************************************************
 * ProductInstaller::ReadInstallerDefaults
 *
 * Read external installer configuration file, setting various defaults
 *
 * Lines look like:
 *  DefInstallPath=/some/directory
 *  AdminPassword=red
 *  AdminPort=5004
 *  AdminUserID=color
 *  EncoderPassword=red
 *  EncoderUserID=color
 *  EncoderRealm=jedi.dev.prognet.com.EncoderRealm
 *  HTTPPort=8081
 *  InstallService=0
 *  MonitorPassword=red
 *  MonitorPort=5005
 *  PNAPort=5003
 *  RTSPPort=5001
 * 
 * NOTE: If you change this list be sure to updat the -h help text
 * in package_info.h!
 */
int
ProductInstaller::ReadInstallerDefaults(const char* szFile)
{
    InstCfgItem pCfgItem[] =
    {
        //config file settings
        { "DefInstallPath",   (void**)&m_szDestDir,        CFG_STR },
        { "AdminUserID",      (void**)&m_szAdminUser,      CFG_STR },
        { "AdminPassword",    (void**)&m_szAdminPass,      CFG_STR },
        { "AdminRealm",       (void**)&m_szAdminRealm,     CFG_STR },
        { "AdminPort",        (void**)&m_pPorts[ADMIN_PORT].nPort, 
                                                           CFG_INT },
        { "EncoderUserID",    (void**)&m_szEncUser,        CFG_STR },
        { "EncoderPassword",  (void**)&m_szEncPass,        CFG_STR },
        { "EncoderRealm",     (void**)&m_szEncRealm,       CFG_STR },
        { "ContentRealm",     (void**)&m_szContentRealm,   CFG_STR },
        { "MonitorPassword",  (void**)&m_szMonPass,        CFG_STR },
        { "MonitorPort",      (void**)&m_pPorts[MON_PORT].nPort,
                                                           CFG_INT },
        { "HTTPPort",         (void**)&m_pPorts[HTTP_PORT].nPort,
                                                           CFG_INT },
        { "RTSPPort",         (void**)&m_pPorts[RTSP_PORT].nPort,
                                                           CFG_INT },
        { "PNAPort",          (void**)&m_pPorts[PNA_PORT].nPort,
                                                           CFG_INT },
        { "LoggingStyle",     (void**)&m_szLoggingStyle,   CFG_STR },

        //other behavior flags
        { "BackupOldFiles",        (void**)&m_bBackupOldFiles, CFG_BOOL },
        { "SilentInstall",         (void**)&m_bSilent,         CFG_BOOL },
        { "NonInteractiveInstall", (void**)&m_bNonInteractive, CFG_BOOL },
        { "DebugInstall",          (void**)&m_bDebug,          CFG_BOOL },
#ifdef _WIN32
        { "InstallService",   (void**)&m_bNTService,      CFG_BOOL },
        { "Service.Logon",    (void**)&m_szServiceUser,   CFG_STR },  //XXX NT
        { "Service.Name",     (void**)&m_szServiceName,   CFG_STR },  //XXX NT
        { "Service.Password", (void**)&m_szServicePass,   CFG_STR },  //XXX NT
#endif // _WIN32
        //this must be the last entry!
        { 0, 0, CFG_STR }
    };

    return ReadInstallerConfigItems(szFile, (const InstCfgItem*)pCfgItem, NULL);
}


/************************************************************************
 * VersionBanner - Return the version banner string
 */
const char*
ProductInstaller::VersionBanner(void)
{
    static char* pMsg=0;
    if (!pMsg)
    {
        pMsg = new char[256];
        sprintf (pMsg, "\n%s Installer", PackageInfo::VersionString());
    }
    return pMsg;
}


/************************************************************************
 * SetDefaultDir:
 * Sets the global directory to the default value.
 * On Unix platforms, just the current working directory.
 * On Windows, it's the current install directory if there is one, 
 * otherwise a subdir of Program Files.
 */
#ifdef _WIN32
extern const char* SZ_DEFAULT_PATH;

void
ProductInstaller::SetDefaultDir(char* &szDestDir)
{
    HX_VECTOR_DELETE(szDestDir);

    szDestDir = new char[MAX_PATH+1];
    if(!szDestDir)
    {
        return;
    }

    HKEY hKey;
    int res;
    DWORD dwSize;
    DWORD dwType;

    /* 
     * Default to "Program Files\Prog Name".
     */

    // Open the Software\Windows\CurrentVersion key
    res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_PROGFILES_KEY, 0, KEY_READ, 
            &hKey);

    if(res == ERROR_SUCCESS)
    {
        dwSize = MAX_PATH+1;

        // Get the ProgramFilesDir value
        res = RegQueryValueEx(hKey, SZ_PROGFILES_VAL, NULL, &dwType,
                (LPBYTE)szDestDir, &dwSize);

        RegCloseKey(hKey);
        if(res == ERROR_SUCCESS && dwType == REG_SZ)
        {
            // We got the program files directory, now add the rest
            size_t unLen = strlen(szDestDir);
            if(unLen != 0)
            {
                strncat(szDestDir, SZ_DEFAULT_PATH, MAX_PATH - unLen);
                return;
            }
        }
    }

    /*
     * We couldn't get ProgramFilesDir! just go with '.'
     */
    if(!_getcwd(szDestDir, MAX_PATH))
    {
        // give up.
        szDestDir[0] = '\0';
    }
}

BOOL
ProductInstaller::GetLocalHostName(char*& szHostname)
{
    szHostname = new char[MAX_COMPUTERNAME_LENGTH + 1];
    if(!szHostname)
    {
        return FALSE;
    }

    szHostname[0] = '\0';

    DWORD dwLen = MAX_COMPUTERNAME_LENGTH + 1;
    BOOL bRes = GetComputerName(szHostname, &dwLen);

    if(!bRes && dwLen > MAX_COMPUTERNAME_LENGTH)
    {
        // The string was not big enough
        delete[] szHostname;
        szHostname = new char[dwLen++];
        
        bRes = GetComputerName(szHostname, &dwLen);
    }

    return bRes;
}

#else // _WIN32

void
ProductInstaller::SetDefaultDir(char* &szDestDir)
{
    char szTemp[PATH_MAX];
    getcwd(szTemp, PATH_MAX);
    int i = strlen(szTemp);
    while (i >= 0)
    {
        if (szTemp[i] == '/')
        {
            szTemp[i] = '\0';  //wack off the final (temp) directory
            i=0;
        }
        --i;
    }

    // get the product name and remove whitespace
    const char* pProdName = ProductVersion::ProductName();
    char szTemp2[PATH_MAX];
    char* pCursor = szTemp2;
    while (*pProdName && pCursor != szTemp2 + PATH_MAX)
    {
	if (!isspace(*pProdName))
	{
	    *pCursor = *pProdName;
	    pCursor++;
	}
	pProdName++;
    }
    *pCursor = 0;

    // format the default install path
    int nLen = strlen(szTemp) + strlen(szTemp2) + 2; // path sep + null term
    szDestDir = new char[nLen];
    if (szDestDir)
    {
	SafeSprintf(szDestDir, nLen, "%s/%s", szTemp, szTemp2);
    }
    else
    {
	szDestDir = NULL;
    }
}

BOOL
ProductInstaller::GetLocalHostName(char*& szHostname)
{
    szHostname = new char[1024];
    if(!szHostname)
    {
        return FALSE;
    }
    szHostname[0] = '\0';
    BOOL bRes = gethostname(szHostname, 1024) == 0;
    szHostname[1023] = '\0';

    return bRes;
}

#endif // _WIN32

/************************************************************************
 * ProductInstaller::BackupIfPresent
 *
 * If the named file or directory foo exists, rename it to foo.old.
 */
void
ProductInstaller::BackupIfPresent(const char* szFile)
{
    char szCmd[1024];
    sprintf (szCmd, "rm -rf %s.old 2>/dev/null", szFile);
    System (szCmd);

    char szFile2[1024];
    sprintf (szFile2, "%s.old", szFile);
    rename (szFile, szFile2);
}



/************************************************************************
 * ProductInstaller::ReadInstallerConfigItems
 *
 * Read external installer configuration file, setting various defaults
 *
 * Note - This is for reading an _installer_ config file, not a product
 * config file.
 *
 * Lines look like:
 *  DefInstallPath=/some/directory
 *  AdminUserID=super
 *  AdminPassword=duper
 *  AdminPort=4004
 *  EncoderUserID=super
 *  EncoderPassword=super
 *  EncoderRealm=foo.bar.com.EncoderRealm
 *  HTTPPort=8081
 *  InstallService=0
 *  MonitorPassword=red
 *  MonitorPort=4005
 *  PNAPort=4003
 *  RTSPPort=4001
 */
int
ProductInstaller::ReadInstallerConfigItems(const char*        szFile,
                                          const InstCfgItem* pCfg,
                                          ConsoleUI*         pCUI)
{
    FILE* fp = fopen (szFile, "r");
    if (!fp)
    {
        return -1;
    }

    char szName[1024];
    char szValue[1024];
    char szLine[1024];
    char* p = szLine;
    char* p2 = szName;
    int n=0;

    while (fgets (szLine, 1024, fp))
    {
        // skip leading whitespace
        p = szLine;
        while (*p == ' ')
        {
            ++p;
        }

        // copy the name
        p2 = szName;
        while (isalnum(*p))
        {
            *p2 = *p;
            ++p;
            ++p2;
        }
        *p2 = '\0';

        // skip {whitespace}{equals}{whitespace} sequence
        while (*p == ' ' || *p == '\t' || *p == '=')
        {
            ++p;
        }

        // copy the value
        p2 = szValue;
        
        while (*p && *p != '\n' && *p != '\r')
        {
            *p2 = *p;
            ++p;
            ++p2;
        }

        // skip trailing whitespace
        while(p2 > szValue && isspace(*(p2-1)))
        {
            p2--;
        }
        *p2 = '\0';


        const InstCfgItem* pCfgItem = pCfg;
        while (pCfgItem && pCfgItem->szName)
        {
            if (!strcasecmp(szName, pCfgItem->szName))
            {
                if (pCUI)
                    pCUI->ShowMessage ("      :Config: %s = %s\n",
                                      pCfgItem->szName, szValue);
                if (pCfgItem->eType == CFG_BOOL || 
                    pCfgItem->eType == CFG_INT)
                {
                    if (pCfgItem->ppValue)
                    {
                        *(int*)(pCfgItem->ppValue) = atoi(szValue);
                    }
                }
                else if (pCfgItem->eType == CFG_STR)
                {
                    if ((n = strlen(szValue)) && pCfgItem->ppValue)
                    {
                        char* pValue = new char[n+1];
                        strcpy(pValue, szValue);
                        *(pCfgItem->ppValue) = pValue;
                    }
                }
            }
            ++pCfgItem;
        }
    }

    fclose(fp);
    return 0;
}


/************************************************************************
 * ProductInstaller::SetInstPort
 */
void
ProductInstaller::SetInstPort(int nPortID, int nPort)
{
    m_pPorts[nPortID].nPort = nPort;
    m_pPorts[nPortID].bSet = TRUE;
}

/************************************************************************
 * ProductInstaller::ValidPort - Checks whether szPort is a valid port 
 * number and if so, set nPort to its int value.
 */
BOOL
ProductInstaller::ValidPort(const char* szPort, int& nPort)
{
    // No letters, symbols, etc.
    for(const char* p = szPort; *p != '\0'; p++)
    {
        if(!isdigit(*p))
        {
            return FALSE;
        }
    }

    nPort = atoi(szPort);

    // Make sure it's in a valid range
    return (nPort > 0 && nPort <= 0xffff);
}

/************************************************************************
 * ProductInstaller::CheckDupePort - checks if nPort has already been 
 * selected. Sets szName to the string name of the port (RTSP, HTTP, etc.).
 * nPortID should be the intended location - it is ignored in the check.
 */
BOOL
ProductInstaller::CheckDupePort(int nPort, int nPortID, const char*& szName)
{
    for(int i = 0; i < (int)m_ulNumPorts; i++)
    {
        if(i != nPortID && m_pPorts[i].nPort == nPort && m_pPorts[i].bSet)
        {
            szName = m_pPorts[i].szName;
            return TRUE;
        }
    }

    return FALSE;
}

/************************************************************************
 * ProductInstaller::StartWSA - start winsock
 */
#ifdef _WIN32
BOOL
ProductInstaller::InitWinsock()
{
    if(!m_bWSAStarted)
    {
        WSADATA wsadata;
        if(WSAStartup(MAKEWORD(1,1), &wsadata) == 0)
        {
            m_bWSAStarted = TRUE;
        }
    }
    return m_bWSAStarted;
}
#endif // _WIN32

/************************************************************************
 * ProductInstaller::PortInUse - checks if nPort is currently in use.
 */
BOOL
ProductInstaller::PortInUse(int nPort)
{
#ifdef _WIN32
    if(!InitWinsock())
    {
        return FALSE;
    }

    SOCKET s;
#else
    int s;
#endif // WIN32

    s = socket(PF_INET, SOCK_STREAM, 0);

#ifdef _WIN32
    if(s == INVALID_SOCKET)
#else
    if(s == -1)
#endif // _WIN32
    {
        return FALSE;
    }

    BOOL bInUse = FALSE;

    sockaddr_in addr;
    addr.sin_family = PF_INET;
    addr.sin_port = htons(nPort);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    // Try to bind the port and see if it fails with EADDRINUSE.
    // Ignore other errors.
    if(bind(s, (sockaddr*)&addr, sizeof(addr)))
    {
#ifdef _WIN32
        bInUse = (WSAGetLastError() == WSAEADDRINUSE);
#else
        bInUse = (errno == EADDRINUSE);
#endif // _WIN32

        // If it failed with some other error, just try to connect.
        // If we can, then it must be in use.
        if(!bInUse)
        {
            bInUse = (connect(s, (sockaddr*)&addr, sizeof(addr)) == 0);
        }
    }

#ifdef _WIN32
    closesocket(s);
#else
    close(s);
#endif // _WIN32

    return bInUse;
}

/************************************************************************
 * ProductInstaller::CleanUp
 */
#ifdef _UNIX
void
ProductInstaller::CleanUp(const char* szDestDir)
{
    // We do this twice because on HP-UX we can't delete a program
    // we're currently running.  So we do all the deleting we can,
    // then kick off a subshell that waits for us to exit then
    // deletes what's left again.
    char szCmd[4096];
    sprintf (szCmd, "(cd ..; rm -rf hxsetup %s/Bin/setup 2> /dev/null); ",
             szDestDir);
    System(szCmd);

    struct stat info;
    if (stat("../hxsetup", &info) == 0)
    {
        sprintf (szCmd,
            "(cd ..; sleep 2; rm -rf hxsetup %s/Bin/setup >/dev/null 2>&1) &",
            szDestDir);
        System(szCmd);
    }
}
#else
void
ProductInstaller::CleanUp(const char* szDestDir)
{
    
}
#endif // _UNIX


/************************************************************************
 * ProductInstaller::CleanUpAndExit
 */
#ifdef _CONSOLE
void
ProductInstaller::CleanUpAndExit(int nErr, ConsoleUI* pCUI)
{
    if(!m_bSilent)
    {
        pCUI->ShowMessage("Cleaning up installation files...");
        pCUI->FlushOutput();
        pCUI->StartPrintingDots();
    }

    CleanUp(m_szDestDir);

    if(!m_bSilent)
    {
        pCUI->StopPrintingDots();
        pCUI->ShowMessage("\nDone.\n\n");
    }
    exit(nErr);
}
#else
void
ProductInstaller::CleanUpAndExit(int nErr, ConsoleUI* pCUI)
{
    CleanUp(m_szDestDir);
    exit(nErr);
}
#endif // _CONSOLE


int
ProductInstaller::System(const char* szCmd)
{
    if(!m_bSilent)
    {
        xprintf (("\nexecuting: %s\n", szCmd));
    }
    return system(szCmd);
}


void
ProductInstaller::SetInstallLog(const char* pPath)
{
    HX_VECTOR_DELETE(m_szInstallLog);
    m_szInstallLog = new_string(pPath);
}
