//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
// 
// This program is free software.  The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
// 
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please see the GNU General Public License
// for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
// 
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
// 
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// twutil.cpp
//

#include "stdtw.h"
#include "twutil.h"

#include "twcrypto/crypto.h"
#include "core/archive.h"
#include "core/serializerimpl.h"
#include "twcrypto/cryptoarchive.h"
#include "fcoreport.h"
#include "twcrypto/keyfile.h"
#include "fco/fcospeclist.h"
#include "fco/genreswitcher.h"
#include "core/errorbucketimpl.h"
#include "core/cmdlineparser.h"
#include "configfile.h"
#include "core/fileheader.h"
#include "core/usernotify.h"
#include "policyfile.h"
#include "core/serstring.h"
#include "headerinfo.h"
#include "systeminfo.h"
#include "core/errorgeneral.h"
#include "fco/parsergenreutil.h"
#include "fco/twfactory.h"
#include "fco/fconame.h"
#include "fcodatabasefile.h"
#include "util/fileutil.h"
#include "core/stringutil.h"
#include "tw/twstrings.h"
#include "tw/twerrors.h"
#include "core/ntmbs.h"
#include "core/displayencoder.h"

#ifdef TW_PROFILE
#include "core/tasktimer.h"
#endif

#include "core/fsservices.h" // for the util_IsDir() stuff

#if IS_UNIX
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include "core/tw_signal.h"
int _getch(void);
#endif

#ifdef _UNICODE // right now we only need this for unicode
#include <mbstring.h> 
#endif

// constants
static const char* POLICY_FILE_MAGIC_8BYTE = "#POLTXT\n";
static const char* CONFIG_FILE_MAGIC_8BYTE = "#CFGTXT\n";
static const uint32 CURRENT_FIXED_VERSION  = 0x02020000;


///////////////////////////////////////////////////////////////////////////////
// WriteObjectToArchive -- called from WriteObject, does most of the work
///////////////////////////////////////////////////////////////////////////////
static void WriteObjectToArchive(cArchive &arch, const TCHAR* filename, // filename is used only for issuing error messages
								 const iTypedSerializable* pObjHeader, const iTypedSerializable& obj, 
								 cFileHeader& fileHeader, bool bEncrypt, const cElGamalSigPrivateKey* pPrivateKey)
{
    try
    {
        // Set file version.
        // If we in the future we wish to support reading databases of different versions,
        // we will have to move this set to outside WriteObject().
        fileHeader.SetVersion(CURRENT_FIXED_VERSION);

	    fileHeader.SetEncoding(bEncrypt ? cFileHeader::ASYM_ENCRYPTION : cFileHeader::COMPRESSED);

	    {
		    cSerializerImpl fhSer(arch, cSerializerImpl::S_WRITE, filename);
		    fileHeader.Write(&fhSer);
	    }

	    if (bEncrypt)
	    {
            cElGamalSigArchive cryptoArchive;
	        cryptoArchive.SetWrite(&arch, pPrivateKey);
		    cSerializerImpl ser(cryptoArchive, cSerializerImpl::S_WRITE, filename);
		    ser.Init();
		    if(pObjHeader)
			    ser.WriteObject(pObjHeader);
		    ser.WriteObject(&obj);
		    ser.Finit();
		    cryptoArchive.FlushWrite();
	    }
	    else
	    {
		    // not encrypted
            cNullCryptoArchive cryptoArchive;
	        cryptoArchive.Start(&arch);
		    cSerializerImpl ser(cryptoArchive, cSerializerImpl::S_WRITE, filename);
		    ser.Init();
		    if(pObjHeader)
			    ser.WriteObject(pObjHeader);
		    ser.WriteObject(&obj);
		    ser.Finit();
		    cryptoArchive.Finish();
	    }
		    
    }
    catch (eError& e)
    {
		throw ePoly(
			e.GetID(), 
			cErrorUtil::MakeFileError( e.GetMsg(), filename ), 
			e.GetFlags() );
    }
}

///////////////////////////////////////////////////////////////////////////////
// WriteObject -- template class that writes an object to disk (ie -- report, 
//		db, etc) either encrypted or unencrypted. 
//      The only requrement on the object is that it is typed serializable.
//		errorMsg is a number that can be passed to iUserString to indicate an
//		appropriate error message if the object fails to load
//
// 10/30 -- this function has been expanded to take two objects -- a header and 
//		an object. Both are typed serializable, but the header can be NULL if 
//		none is desired. 
///////////////////////////////////////////////////////////////////////////////
static void WriteObject(const TCHAR* filename, const iTypedSerializable* pObjHeader, const iTypedSerializable& obj, 
						cFileHeader& fileHeader, bool bEncrypt, const cElGamalSigPrivateKey* pPrivateKey)
{
    cDebug d("WriteObject");
	d.TraceDebug(_T("Writing %s to file %s\n"), obj.GetType().AsString(), filename);

	ASSERT(pPrivateKey || (! bEncrypt));

	cFileArchive arch;

	if (!cFileUtil::IsRegularFile(filename) && cFileUtil::FileExists(filename))
		throw eArchiveNotRegularFile( filename );

    try
    {
	    arch.OpenReadWrite( filename );
    }
    catch (eArchive&)
    {
        // probably better to rethrow this as a write failed exception
		throw eArchiveWrite( filename, iFSServices::GetInstance()->GetErrString() ) ;
    }

	WriteObjectToArchive(arch, filename, pObjHeader, obj, fileHeader, bEncrypt, pPrivateKey);

	arch.Close();
}


///////////////////////////////////////////////////////////////////////////////
// WriteObjectToArchive -- called from WriteObject, does most of the work
///////////////////////////////////////////////////////////////////////////////
static void ReadObjectFromArchive(cArchive &arch, const TCHAR* objFileName, iTypedSerializable* pObjHeader, iTypedSerializable& obj, const cFileHeaderID& fhid, const cElGamalSigPublicKey* pPublicKey, bool& bEncrypted)
{
    cFileHeader fileHeader;

    {
		cSerializerImpl fhSer(arch, cSerializerImpl::S_READ, objFileName);
		fileHeader.Read(&fhSer);
	}

    // check for a mismatched header
    if (fileHeader.GetID() != fhid)
        ThrowAndAssert(eSerializerInputStreamFmt(_T(""), objFileName, eSerializer::TY_FILE));

    // Check file version.
    // If we in the future we wish to support reading objects of different versions,
    // we will have to move this check to outside ReadObject().
    if (fileHeader.GetVersion() != CURRENT_FIXED_VERSION)
        ThrowAndAssert(eSerializerVersionMismatch(_T(""), objFileName, eSerializer::TY_FILE));

    try 
    {
        // switch on the type of encoding...
	    if(fileHeader.GetEncoding() == cFileHeader::ASYM_ENCRYPTION)
	    {
		    // tell the user the db is encrypted
		    iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, TSS_GetString( cTW, tw::STR_FILE_ENCRYPTED).c_str());
		    bEncrypted = true;

            if (pPublicKey == 0)
                ThrowAndAssert(eSerializerEncryption(_T("")));

		    cElGamalSigArchive cryptoArchive;
		    cryptoArchive.SetRead(&arch, pPublicKey);

		    cSerializerImpl ser(cryptoArchive, cSerializerImpl::S_READ, objFileName);			
		    ser.Init();
		    if(pObjHeader)
			    ser.ReadObject(pObjHeader);
		    ser.ReadObject(&obj);
		    ser.Finit();
	    }
	    else if(fileHeader.GetEncoding() == cFileHeader::COMPRESSED)
	    {
		    //not encrypted db...
		    bEncrypted = false;

		    cNullCryptoArchive cryptoArchive;
		    cryptoArchive.Start(&arch);

		    cSerializerImpl ser(cryptoArchive, cSerializerImpl::S_READ, objFileName);			
		    ser.Init();
		    if(pObjHeader)
			    ser.ReadObject(pObjHeader);
		    ser.ReadObject(&obj);
		    ser.Finit();
	    }
	    else
		    // unknown encoding...
		    ThrowAndAssert(eSerializerInputStreamFmt(_T("")));
    }
    catch (eError& e)
    {
        // include filename in error msg
		throw ePoly( 
			e.GetID(), 
			cErrorUtil::MakeFileError( e.GetMsg(), objFileName ), 
			e.GetFlags() );
    }
}

///////////////////////////////////////////////////////////////////////////////
// ReadObject -- writes an object from disk, either encrypted or not, that was
//		written using WriteObject above. 
///////////////////////////////////////////////////////////////////////////////
static void ReadObject(const TCHAR* objFileName, iTypedSerializable* pObjHeader, iTypedSerializable& obj, const cFileHeaderID& fhid, const cElGamalSigPublicKey* pPublicKey, bool& bEncrypted)
{
    cDebug d("ReadObject");
	d.TraceDebug(_T("Reading %s from file %s\n"), obj.GetType().AsString(), objFileName);
	
	cFileArchive arch;
	arch.OpenRead( objFileName );

	ReadObjectFromArchive(arch, objFileName, pObjHeader, obj, fhid, pPublicKey, bEncrypted);

}

///////////////////////////////////////////////////////////////////////////////
// IsObjectEncrypted
///////////////////////////////////////////////////////////////////////////////
bool cTWUtil::IsObjectEncrypted( const TCHAR* objFileName, const cFileHeaderID& fhid, const TSTRING& errorMsg )
{
    bool fEncrypted = false;
    cDebug d("IsObjectEncrypted");
	d.TraceDebug(_T("Reading from file %s\n"), objFileName);
	
	try
	{
		cFileArchive arch;
		arch.OpenRead( objFileName );

        cFileHeader fileHeader;
		cSerializerImpl fhSer(arch, cSerializerImpl::S_READ, objFileName);
		fileHeader.Read(&fhSer);

        // check for a mismatched header
        if (fileHeader.GetID() != fhid)
            ThrowAndAssert(eSerializerInputStreamFmt(_T(""), objFileName, eSerializer::TY_FILE));

		// switch on the type of encoding...
		if(fileHeader.GetEncoding() == cFileHeader::ASYM_ENCRYPTION)
		{
            fEncrypted = true;
		}
		else if(fileHeader.GetEncoding() == cFileHeader::COMPRESSED)
		{
            fEncrypted = false;
		}
		else
			// unknown encoding...
			ThrowAndAssert(eSerializerInputStreamFmt(_T(""), objFileName, eSerializer::TY_FILE));
	}
	catch(eArchive& e)
	{
        // Note: Output to TCERR is O.K. here, it is documented that this is what this function does
		TSTRING msg = e.GetMsg();
		if( ! msg.empty() )
			msg += _T("\n");
		msg += errorMsg;

		cTWUtil::PrintErrorMsg(ePoly(e.GetID(), msg, e.GetFlags() ));
		ThrowAndAssert(ePoly());
	}
	catch(eSerializer& e)
	{
        // Note: Output to TCERR is O.K. here, it is documented that this is what this function does
		TSTRING msg = e.GetMsg();
		if( ! msg.empty() )
			msg += _T("\n");
		msg += errorMsg;

		cTWUtil::PrintErrorMsg(ePoly(e.GetID(), msg, e.GetFlags() ));
		ThrowAndAssert(ePoly());
	}

    return( fEncrypted );
}

bool cTWUtil::IsObjectEncrypted( cArchive &arch, const cFileHeaderID& fhid, const TSTRING& errorMsg )
{
    bool fEncrypted = false;
    cDebug d("IsObjectEncrypted");
	d.TraceDebug(_T("Reading from archive\n"));
	
	try
	{
        cFileHeader fileHeader;
		cSerializerImpl fhSer(arch, cSerializerImpl::S_READ, TSS_GetString( cTW, tw::STR_MEMORY_MAPPED_FILENAME).c_str());
		fileHeader.Read(&fhSer);

        // check for a mismatched header
        if (fileHeader.GetID() != fhid)
            ThrowAndAssert(eSerializerInputStreamFmt(_T(""), TSS_GetString( cTW, tw::STR_MEMORY_MAPPED_FILENAME).c_str(), eSerializer::TY_FILE));

		// switch on the type of encoding...
		if(fileHeader.GetEncoding() == cFileHeader::ASYM_ENCRYPTION)
		{
            fEncrypted = true;
		}
		else if(fileHeader.GetEncoding() == cFileHeader::COMPRESSED)
		{
            fEncrypted = false;
		}
		else
			// unknown encoding...
			ThrowAndAssert(eSerializerInputStreamFmt(_T(""), TSS_GetString( cTW, tw::STR_MEMORY_MAPPED_FILENAME).c_str(), eSerializer::TY_FILE));
	}
	catch(eArchive& e)
	{
        // Note: Output to TCERR is O.K. here, it is documented that this is what this function does
		TSTRING msg = e.GetMsg();
		if( ! msg.empty() )
			msg += _T("\n");
		msg += errorMsg;

		cTWUtil::PrintErrorMsg(ePoly(e.GetID(), msg, e.GetFlags() ));
		ThrowAndAssert(ePoly());
	}
	catch(eSerializer& e)
	{
        // Note: Output to TCERR is O.K. here, it is documented that this is what this function does
		TSTRING msg = e.GetMsg();
		if( ! msg.empty() )
			msg += _T("\n");
		msg += errorMsg;

		cTWUtil::PrintErrorMsg(ePoly(e.GetID(), msg, e.GetFlags() ));
		ThrowAndAssert(ePoly());
	}

    return( fEncrypted );
}


///////////////////////////////////////////////////////////////////////////////
// WriteDatabase
///////////////////////////////////////////////////////////////////////////////
void cTWUtil::WriteDatabase(const TCHAR* filename, cFCODatabaseFile& db, bool bEncrypt, const cElGamalSigPrivateKey* pPrivateKey)
{
	cFileHeader fileHeader;
    fileHeader.SetID(db.GetFileHeaderID());
    
    // I am almost positive that this does nothing, WriteObject() sets the version in the cFileHeader - Jun 8, 1999 - dmb
    //fileHeader.SetVersion(1);

#ifdef TW_PROFILE
	cWin32TaskTimer timer(_T("Write Database"));
	timer.Start();
#endif
	
	WriteObject(filename, 0, db, fileHeader, bEncrypt, pPrivateKey);

#ifdef TW_PROFILE
	timer.Stop();
#endif

	iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_WRITE_DB_FILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( filename ).c_str() );
}

///////////////////////////////////////////////////////////////////////////////
// ReadDatabase
///////////////////////////////////////////////////////////////////////////////
void cTWUtil::ReadDatabase(const TCHAR* filename, cFCODatabaseFile& db, const cElGamalSigPublicKey* pPublicKey, bool& bEncrypted)
{
	iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_OPEN_DB_FILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( filename ).c_str() );

#ifdef TW_PROFILE
	cWin32TaskTimer timer("cTWUtil::ReadDatabase");
	timer.Start();
#endif

    ReadObject(filename, 0, db, cFCODatabaseFile::GetFileHeaderID(), pPublicKey, bEncrypted);

#ifdef TW_PROFILE
	timer.Stop();
#endif
}

///////////////////////////////////////////////////////////////////////////////
// WriteReport
///////////////////////////////////////////////////////////////////////////////
void cTWUtil::WriteReport(const TCHAR* filename, const cFCOReportHeader& reportHeader, const cFCOReport& r, bool bEncrypt, const cElGamalSigPrivateKey* pPrivateKey)
{
    cFileHeader fileHeader;
    fileHeader.SetID(cFCOReport::GetFileHeaderID());

	WriteObject(filename, &reportHeader, r, fileHeader, bEncrypt, pPrivateKey);

	iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_WRITE_REPORT_FILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( filename ).c_str() );
}


void cTWUtil::WriteReport(cArchive &archive, const cFCOReportHeader& reportHeader, const cFCOReport& r, bool bEncrypt, const cElGamalSigPrivateKey* pPrivateKey)
{
    cFileHeader fileHeader;
    fileHeader.SetID(cFCOReport::GetFileHeaderID());

	TSTRING filename = TSS_GetString( cTW, tw::STR_MEMORY_MAPPED_FILENAME);

	WriteObjectToArchive(archive, filename.c_str(), &reportHeader, r, fileHeader, bEncrypt, pPrivateKey);

	iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_WRITE_REPORT_FILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( filename ).c_str() );
}

///////////////////////////////////////////////////////////////////////////////
// ReadReport
///////////////////////////////////////////////////////////////////////////////
void cTWUtil::ReadReport(const TCHAR* reportFileName, cFCOReportHeader& reportHeader, cFCOReport& r, const cElGamalSigPublicKey* pPublicKey, bool silent, bool& bEncrypted)
{
	iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_OPEN_REPORT_FILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( reportFileName ).c_str() );
    
    ReadObject(reportFileName, &reportHeader, r, cFCOReport::GetFileHeaderID(), pPublicKey, bEncrypted);
}

void cTWUtil::ReadReport(cArchive &archive, cFCOReportHeader& reportHeader, cFCOReport& r, const cElGamalSigPublicKey* pPublicKey, bool silent, bool& bEncrypted)
{
	TSTRING filename = TSS_GetString( cTW, tw::STR_MEMORY_MAPPED_FILENAME);

	iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_OPEN_REPORT_FILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( filename ).c_str());
    
    ReadObjectFromArchive(archive, filename.c_str(), &reportHeader, r, cFCOReport::GetFileHeaderID(), pPublicKey, bEncrypted);
}

///////////////////////////////////////////////////////////////////////////////
// UpdatePolicyFile
///////////////////////////////////////////////////////////////////////////////
void cTWUtil::UpdatePolicyFile(const TCHAR* destFileName, const TCHAR* srcFileName, bool bEncrypt, const cElGamalSigPrivateKey* pPrivateKey)
{
	cFileArchive src;
    src.OpenRead(srcFileName); // note: eArchive may float up

    std::string policyText;
    policyText.resize(src.Length());

    src.ReadBlob((void*)policyText.data(), src.Length());

	// NOTE -- this method will emit a message to stdout when the file has been written
    cTWUtil::WritePolicyText(destFileName, policyText, bEncrypt, pPrivateKey); 
}

TSTRING cTWUtil::GetCfgFilePath(const cCmdLineParser& cmdLine, int cfgFileID)
{

  TSTRING cfgFilePath;
  bool fConfigOnCmdLine = false;

  // let's see if this was specified on the command line...
  cCmdLineIter iter(cmdLine);
  for(iter.SeekBegin(); ! iter.Done(); iter.Next())
    {
		if(iter.ArgId() == cfgFileID)
        {
          ASSERT(iter.NumParams() > 0);
          cfgFilePath = iter.ParamAt(0);
          fConfigOnCmdLine = true;
        }
    }
    
  // OK, now we need to figure out where to find the config file.
  // If the location was specified on the command line, get the full path to it.
  // otherwise, the location is the dir that this exe is in.
  if( fConfigOnCmdLine )
    {
		TSTRING pathOut;
		iFSServices::GetInstance()->FullPath( pathOut, cfgFilePath ) ;
		cfgFilePath = pathOut;
    }
  else
    {
		iFSServices::GetInstance()->FullPath(	cfgFilePath, TSS_GetString(cTW, tw::STR_DEF_CFG_FILENAME),
                                             cSystemInfo::GetExeDir() ) ;
    }
  

   cFileUtil::TestFileExists(cfgFilePath);
      
  
   return (cfgFilePath);
}

///////////////////////////////////////////////////////////////////////////////
// OpenConfigFile -- opens the config file, either from a known location or 
//		from the location specified on the command line. Returns false if it fails.
//      cfgFileID is the ID of the config file switch
//		TODO -- should this guy spit the error to cerr or fill up an error bucket?
/////////////////////////////////////////////////////////////////////////////////
void cTWUtil::OpenConfigFile(cConfigFile& cf, cCmdLineParser& cmdLine, int cfgFileID, cErrorBucket& errorBucket, TSTRING& configFileOut)
{

   configFileOut = GetCfgFilePath(cmdLine, cfgFileID);

   // ok, now let's load it!
   TSTRING configText;
   cTWUtil::ReadConfigText(configFileOut.c_str(), configText);
   cf.ReadString( configText );        
}

///////////////////////////////////////////////////////////////////////////////
// WriteConfigText
//      eArchive is thrown if filename can not be opened
//      eSerializer is thrown if reading or writing fails

void cTWUtil::WriteConfigText(const TCHAR* filename, const TSTRING configText, bool bEncrypt, const cElGamalSigPrivateKey* pPrivateKey)
{
    cSerializableNString nstring;

    nstring.mString = CONFIG_FILE_MAGIC_8BYTE;

    std::string ns;
    cStringUtil::Convert( ns, configText );
    nstring.mString += ns;

    cFileHeader fileHeader;
    fileHeader.SetID(cConfigFile::GetFileHeaderID());
    
    fileHeader.SetVersion(CURRENT_FIXED_VERSION);

    if (bEncrypt)
    {
        ASSERT(pPrivateKey != 0);
        cElGamalSigPublicKey publicKey(*pPrivateKey);
        fileHeader.GetBaggage().MapArchive(0, publicKey.GetWriteLen());
        publicKey.Write(fileHeader.GetBaggage().GetMap());
    }

    WriteObject(filename, NULL, nstring, fileHeader, bEncrypt, pPrivateKey);

	iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_WRITE_CONFIG_FILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( filename ).c_str() );
}

void cTWUtil::WriteConfigText(cArchive &archive, const TSTRING configText, bool bEncrypt, const cElGamalSigPrivateKey* pPrivateKey)
{
    cSerializableNString nstring;

    nstring.mString = CONFIG_FILE_MAGIC_8BYTE;

	TSTRING filename = TSS_GetString( cTW, tw::STR_MEMORY_MAPPED_FILENAME);

    std::string ns;
    cStringUtil::Convert( ns, configText );
    nstring.mString += ns;

    cFileHeader fileHeader;
    fileHeader.SetID(cConfigFile::GetFileHeaderID());
    
    fileHeader.SetVersion(CURRENT_FIXED_VERSION);

    if (bEncrypt)
    {
        ASSERT(pPrivateKey != 0);
        cElGamalSigPublicKey publicKey(*pPrivateKey);
        fileHeader.GetBaggage().MapArchive(0, publicKey.GetWriteLen());
        publicKey.Write(fileHeader.GetBaggage().GetMap());
    }

    WriteObjectToArchive(archive, filename.c_str(), NULL, nstring, fileHeader, bEncrypt, pPrivateKey);

	iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_WRITE_CONFIG_FILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( filename ).c_str());
}


///////////////////////////////////////////////////////////////////////////////
// ReadConfigText
//      eArchive is thrown if filename can not be opened
//      eSerializer is thrown if reading or writing fails
//      eConfigFile is thrown if config file does not parse correctly during reading

void cTWUtil::ReadConfigText(const TCHAR* filename, TSTRING& configText, cArchive* pBaggage)
{
// TODO -- neat up this function; try to use LoadObject() above...

    cSerializableNString nstring;

    // This was coppied from ReadObject().  We need to use the baggage of the
    // file header to obtain the public key, thus the special casing.
    cDebug d("ReadConfigText");
	d.TraceDebug(_T("Reading %s from file %s\n"), nstring.GetType().AsString(), filename);
	
	iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_OPEN_CONFIG_FILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( filename ).c_str() );

	cFileArchive arch;
	arch.OpenRead( filename );

    cFileHeader fileHeader;

    try
    {
        cSerializerImpl fhSer(arch, cSerializerImpl::S_READ);
        fileHeader.Read(&fhSer);
    }
    catch (eError&)
    {
        throw eSerializerInputStreamFmt(_T(""), filename, eSerializer::TY_FILE);
    }

#if 0	// XXX: This is broken, what the h*ll are they trying to write here? -PH
    d.TraceDebug("Found a file header of type %d.\n", fileHeader.GetEncoding());
#endif

    // check for a mismatched header
    if (fileHeader.GetID() != cConfigFile::GetFileHeaderID())
        throw eSerializerInputStreamFmt(_T(""), filename, eSerializer::TY_FILE);

    // check the version
    if (fileHeader.GetVersion() != CURRENT_FIXED_VERSION)
        throw eSerializerVersionMismatch(_T(""), filename, eSerializer::TY_FILE);

    // switch on the type of encoding...
	if(fileHeader.GetEncoding() == cFileHeader::ASYM_ENCRYPTION)
	{
        d.TraceDebug("Config file is compressed, public key len %d.\n", fileHeader.GetBaggage().Length());

		// tell the user the db is encrypted
		iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, TSS_GetString(cTW, tw::STR_FILE_ENCRYPTED).c_str());
        iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, TSS_GetString(cTW, tw::STR_NEWLINE).c_str());

        ASSERT(fileHeader.GetBaggage().Length() > 0);
        if (fileHeader.GetBaggage().Length() <= 0)
            ThrowAndAssert(eSerializerInputStreamFmt(_T(""), filename, eSerializer::TY_FILE));

        fileHeader.GetBaggage().MapArchive(0, fileHeader.GetBaggage().Length());

        cElGamalSigPublicKey publicKey(fileHeader.GetBaggage().GetMap());

		cElGamalSigArchive cryptoArchive;
		cryptoArchive.SetRead(&arch, &publicKey);

		cSerializerImpl ser(cryptoArchive, cSerializerImpl::S_READ);			
		ser.Init();
		ser.ReadObject(&nstring);
		ser.Finit();

		// copy the baggage into the archive, if it was passed in
        // Note: We rely in VerifySiteKey that we only fill out pBaggage if
        // the config file is encrypted.
		//
		if( pBaggage )
		{
			fileHeader.GetBaggage().Seek( 0, cBidirArchive::BEGINNING );
			pBaggage->Copy( &fileHeader.GetBaggage(), fileHeader.GetBaggage().Length() );
		}
	}
	else if(fileHeader.GetEncoding() == cFileHeader::COMPRESSED)
	{
        d.TraceDebug("Config file is not compressed.\n");

		//not encrypted db...
		cNullCryptoArchive cryptoArchive;
		cryptoArchive.Start(&arch);

		cSerializerImpl ser(cryptoArchive, cSerializerImpl::S_READ);			
		ser.Init();
		ser.ReadObject(&nstring);
		ser.Finit();
	}
	else
		// unknown encoding...
		throw eSerializerInputStreamFmt(_T(""), filename, eSerializer::TY_FILE);

    // check 8 byte header
    if (nstring.mString.compare(0, 8*sizeof(byte), CONFIG_FILE_MAGIC_8BYTE) != 0)
        ThrowAndAssert(eSerializerInputStreamFmt(_T(""), filename, eSerializer::TY_FILE));

    // remove 8 byte header
    nstring.mString.assign(nstring.mString.substr(8));

    cStringUtil::Convert( configText, nstring.mString );
}  


void cTWUtil::ReadConfigText(cArchive &arch, TSTRING& configText, cArchive* pBaggage)
{
// TODO -- neat up this function; try to use LoadObject() above...

    cSerializableNString nstring;

	TSTRING filename = TSS_GetString( cTW, tw::STR_MEMORY_MAPPED_FILENAME);

    // This was coppied from ReadObject().  We need to use the baggage of the
    // file header to obtain the public key, thus the special casing.
    cDebug d("ReadConfigText");
	d.TraceDebug(_T("Reading %s from file %s\n"), nstring.GetType().AsString(), filename.c_str());
	
	iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_OPEN_CONFIG_FILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( filename ).c_str());


    cFileHeader fileHeader;

    try
    {
        cSerializerImpl fhSer(arch, cSerializerImpl::S_READ);
        fileHeader.Read(&fhSer);
    }
    catch (eError&)
    {
        throw eSerializerInputStreamFmt(_T(""), filename.c_str(), eSerializer::TY_FILE);
    }

#if 0	// XXX: This is broken, how can you convert a class to an int??? -PH
    d.TraceDebug("Found a file header of type %d.\n", fileHeader.GetEncoding());
#endif

    // check for a mismatched header
    if (fileHeader.GetID() != cConfigFile::GetFileHeaderID())
        throw eSerializerInputStreamFmt(_T(""), filename.c_str(), eSerializer::TY_FILE);

    // check the version
    if (fileHeader.GetVersion() != CURRENT_FIXED_VERSION)
        throw eSerializerVersionMismatch(_T(""), filename.c_str(), eSerializer::TY_FILE);

    // switch on the type of encoding...
	if(fileHeader.GetEncoding() == cFileHeader::ASYM_ENCRYPTION)
	{
        d.TraceDebug("Config file is compressed, public key len %d.\n", fileHeader.GetBaggage().Length());

		// tell the user the db is encrypted
		iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, TSS_GetString(cTW, tw::STR_FILE_ENCRYPTED).c_str());
        iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, TSS_GetString(cTW, tw::STR_NEWLINE).c_str());

        ASSERT(fileHeader.GetBaggage().Length() > 0);
        if (fileHeader.GetBaggage().Length() <= 0)
            ThrowAndAssert(eSerializerInputStreamFmt(_T(""), filename.c_str(), eSerializer::TY_FILE));

        fileHeader.GetBaggage().MapArchive(0, fileHeader.GetBaggage().Length());

        cElGamalSigPublicKey publicKey(fileHeader.GetBaggage().GetMap());

		cElGamalSigArchive cryptoArchive;
		cryptoArchive.SetRead(&arch, &publicKey);

		cSerializerImpl ser(cryptoArchive, cSerializerImpl::S_READ);			
		ser.Init();
		ser.ReadObject(&nstring);
		ser.Finit();

		// copy the baggage into the archive, if it was passed in
        // Note: We rely in VerifySiteKey that we only fill out pBaggage if
        // the config file is encrypted.
		//
		if( pBaggage )
		{
			fileHeader.GetBaggage().Seek( 0, cBidirArchive::BEGINNING );
			pBaggage->Copy( &fileHeader.GetBaggage(), fileHeader.GetBaggage().Length() );
		}
	}
	else if(fileHeader.GetEncoding() == cFileHeader::COMPRESSED)
	{
        d.TraceDebug("Config file is not compressed.\n");

		//not encrypted db...
		cNullCryptoArchive cryptoArchive;
		cryptoArchive.Start(&arch);

		cSerializerImpl ser(cryptoArchive, cSerializerImpl::S_READ);			
		ser.Init();
		ser.ReadObject(&nstring);
		ser.Finit();
	}
	else
		// unknown encoding...
		throw eSerializerInputStreamFmt(_T(""), filename.c_str(), eSerializer::TY_FILE);

    // check 8 byte header
    if (nstring.mString.compare(0, 8*sizeof(byte), CONFIG_FILE_MAGIC_8BYTE) != 0)
        ThrowAndAssert(eSerializerInputStreamFmt(_T(""), filename.c_str(), eSerializer::TY_FILE));

    // remove 8 byte header
    nstring.mString.assign(nstring.mString.substr(8));

    cStringUtil::Convert( configText, nstring.mString );
}
  

///////////////////////////////////////////////////////////////////////////////
// Given a filename and the text of a policy file, write an encrypted version
// of the policy file text to disk.  
// Will throw eError on failure.

void cTWUtil::WritePolicyText(const TCHAR* filename, const std::string polText, bool bEncrypt, const cElGamalSigPrivateKey* pPrivateKey)
{
    cSerializableNString nstring;

    // add a 8 byte header we can use to verify decryption
    nstring.mString = POLICY_FILE_MAGIC_8BYTE;

    nstring.mString += polText;

    cFileHeader fileHeader;
    fileHeader.SetID(cPolicyFile::GetFileHeaderID());

    WriteObject(filename, NULL, nstring, fileHeader, bEncrypt, pPrivateKey);

	iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_WRITE_POLICY_FILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( filename ).c_str() );
}

void cTWUtil::WritePolicyText(cArchive &archive, const std::string polText, bool bEncrypt, const cElGamalSigPrivateKey* pPrivateKey)
{
    cSerializableNString nstring;

	TSTRING filename = TSS_GetString( cTW, tw::STR_MEMORY_MAPPED_FILENAME);

    // add a 8 byte header we can use to verify decryption
    nstring.mString = POLICY_FILE_MAGIC_8BYTE;

    nstring.mString += polText;

    cFileHeader fileHeader;
    fileHeader.SetID(cPolicyFile::GetFileHeaderID());

    WriteObjectToArchive(archive, filename.c_str(), NULL, nstring, fileHeader, bEncrypt, pPrivateKey);

	iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_WRITE_POLICY_FILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( filename ).c_str());
}

///////////////////////////////////////////////////////////////////////////////
// ReadPolicyText
//	
// Read the policy file.  Read the text of a policy language into configText
// Will throw eError on failure.
///////////////////////////////////////////////////////////////////////////////
void cTWUtil::ReadPolicyText(const TCHAR* filename, std::string& polText, const cElGamalSigPublicKey* pPublicKey)
{
    cSerializableNString nstring;

	cFileUtil::TestFileExists(filename);

    bool bEncrypted;
    ReadObject(filename, NULL, nstring, cPolicyFile::GetFileHeaderID(), pPublicKey, bEncrypted);

    // check 8 byte header
    if (nstring.mString.compare(0, 8*sizeof(byte), POLICY_FILE_MAGIC_8BYTE) != 0)
        ThrowAndAssert(eSerializerInputStreamFmt(_T(""), filename, eSerializer::TY_FILE));

    // remove 8 byte header
    nstring.mString.assign(nstring.mString.substr(8));

    polText = nstring.mString;
}

void cTWUtil::ReadPolicyText(cArchive &archive, std::string& polText, const cElGamalSigPublicKey* pPublicKey)
{
    cSerializableNString nstring;

	TSTRING filename = TSS_GetString( cTW, tw::STR_MEMORY_MAPPED_FILENAME);

    bool bEncrypted;
    ReadObjectFromArchive(archive, filename.c_str(), NULL, nstring, cPolicyFile::GetFileHeaderID(), pPublicKey, bEncrypted);

    // check 8 byte header
    if (nstring.mString.compare(0, 8*sizeof(byte), POLICY_FILE_MAGIC_8BYTE) != 0)
        ThrowAndAssert(eSerializerInputStreamFmt(_T(""), filename.c_str(), eSerializer::TY_FILE));

    // remove 8 byte header
    nstring.mString.assign(nstring.mString.substr(8));

    polText = nstring.mString;
}

///////////////////////////////////////////////////////////////////////////////
// OpenKeyFile
///////////////////////////////////////////////////////////////////////////////
void cTWUtil::OpenKeyFile(cKeyFile& keyFile, TSTRING fileName)
{
	// first, make sure the file exists...
	cFileUtil::TestFileExists(fileName);
	
	iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, _T("%s%s\n"),
										TSS_GetString( cTW, tw::STR_OPEN_KEYFILE).c_str(), 
                                        cDisplayEncoder::EncodeInline( fileName ).c_str());

    keyFile.ReadFile(fileName.c_str());
}

///////////////////////////////////////////////////////////////////////////////
// CreatePrivateKey
//		we will attempt to get the correct passphrase three times before we
//		give up.
///////////////////////////////////////////////////////////////////////////////
const cElGamalSigPrivateKey* cTWUtil::CreatePrivateKey(cKeyFile& keyFile, const WCHAR16* usePassphrase, KeyType keyType, int nSecs)
{
    ASSERT(keyType == KEY_SITE || keyType == KEY_LOCAL || keyType == KEY_PROVIDED);

	const cElGamalSigPrivateKey* pPrivateKey = NULL;
	wc16_string passphrase;

    if ( usePassphrase ) 
    {   
        // sleep to hinder brute force (dictionary, etc.) attacks
        iFSServices::GetInstance()->Sleep( nSecs );

		passphrase = usePassphrase;

        #ifndef WORDS_BIGENDIAN
        passphrase.swapbytes();
        #endif

		pPrivateKey = keyFile.GetPrivateKey((int8*)passphrase.data(), passphrase.length() * sizeof(WCHAR16));

        if(pPrivateKey)
			return pPrivateKey;

        // if we got here, then a passphrase was provided on the command line that
		// was not correct; this is an error condition.
		//
		if( keyType == KEY_LOCAL )
			throw eTWUtilBadPassLocal();
		else
			throw eTWUtilBadPassSite();
    }

    int count = 0;
	while(count < 3)
	{
		switch (keyType)
        {
        case KEY_LOCAL:
			TCOUT << TSS_GetString( cTW, tw::STR_ENTER_LOCAL_PASSPHRASE);
            break;
        case KEY_SITE:
			TCOUT << TSS_GetString( cTW, tw::STR_ENTER_SITE_PASSPHRASE);
            break;
        case KEY_PROVIDED:
        default:
		    TCOUT << TSS_GetString( cTW, tw::STR_ENTER_PROVIDED_PASSPHRASE);
            break;
        }

		cTWUtil::GetStringNoEcho(passphrase);
		TCOUT << std::endl;

        // sleep to hinder brute force (dictionary, etc.) attacks
        iFSServices::GetInstance()->Sleep( nSecs );

        #ifndef WORDS_BIGENDIAN
        passphrase.swapbytes();
        #endif

		pPrivateKey = keyFile.GetPrivateKey((int8*)passphrase.data(), passphrase.length() * sizeof(WCHAR16));

		if (pPrivateKey)
			break;

		// tell the user that they entered the wrong passphrase
        int strId = (keyType==KEY_LOCAL) ?	tw::STR_ERR_WRONG_PASSPHRASE_LOCAL : 
											tw::STR_ERR_WRONG_PASSPHRASE_SITE;
		TCOUT << TSS_GetString( cTW, strId) << std::endl;
		passphrase.resize(0);
		count++;
	}

	if(! pPrivateKey)
		throw ePoly();

	return pPrivateKey;
}

void cTWUtil::CreatePrivateKey(cPrivateKeyProxy& proxy, cKeyFile& keyFile, const WCHAR16* usePassphrase, KeyType keyType, int nSecs)
{
    ASSERT(keyType == KEY_SITE || keyType == KEY_LOCAL || keyType == KEY_PROVIDED);

    wc16_string passphrase;

    if (usePassphrase != 0)
    {
        // sleep to hinder brute force (dictionary, etc.) attacks
        iFSServices::GetInstance()->Sleep( nSecs );

		passphrase = usePassphrase;

        #ifndef WORDS_BIGENDIAN
        passphrase.swapbytes();
        #endif

        if (proxy.AquireKey(keyFile, (int8*)passphrase.data(), passphrase.length() * sizeof(WCHAR16)))
			return;

        // if we got here, then a passphrase was provided on the command line that
		// was not correct; this is an error condition.
		//
		if( keyType == KEY_LOCAL )
			throw eTWUtilBadPassLocal();
		else
			throw eTWUtilBadPassSite();
    }

    int count = 0;
	while(count < 3)
	{
		switch (keyType)
        {
        case KEY_LOCAL:
			TCOUT << TSS_GetString( cTW, tw::STR_ENTER_LOCAL_PASSPHRASE);
            break;
        case KEY_SITE:
			TCOUT << TSS_GetString( cTW, tw::STR_ENTER_SITE_PASSPHRASE);
            break;
        case KEY_PROVIDED:
        default:
		    TCOUT << TSS_GetString( cTW, tw::STR_ENTER_PROVIDED_PASSPHRASE);
            break;
        }

		cTWUtil::GetStringNoEcho(passphrase);
		TCOUT << std::endl;

        // sleep to hinder brute force (dictionary, etc.) attacks
        iFSServices::GetInstance()->Sleep( nSecs );

        #ifndef WORDS_BIGENDIAN
        passphrase.swapbytes();
        #endif

		if (proxy.AquireKey(keyFile, (int8*)passphrase.data(), passphrase.length() * sizeof(WCHAR16)))
			return;

		// tell the user that they entered the wrong passphrase
        int strId = (keyType==KEY_LOCAL) ?	tw::STR_ERR_WRONG_PASSPHRASE_LOCAL : 
                    (keyType==KEY_SITE)  ?  tw::STR_ERR_WRONG_PASSPHRASE_SITE :
                    tw::STR_ERR_WRONG_PASSPHRASE_LOCAL; // TODO: make this provided

        TCOUT << TSS_GetString( cTW, strId) << std::endl;
		passphrase.resize(0);
		count++;
	}

    throw ePoly();
}

///////////////////////////////////////////////////////////////////////////////
// GetStringNoEcho -- Get a string from the user without echoing it
///////////////////////////////////////////////////////////////////////////////


#if IS_UNIX
static void (*old_SIGINT)(int);
static void (*old_SIGQUIT)(int);
static struct termios Otty;

static void RestoreEcho(int sig)
{
#ifdef _DEBUG
    std::cout << "Caught signal, resetting echo."<< std::endl;
    sleep(2);
#endif

    tcsetattr( 0, TCSAFLUSH, &Otty);
    tw_signal(SIGINT, old_SIGINT);
    tw_signal(SIGQUIT, old_SIGQUIT);

    tw_raise(sig);
}

void cTWUtil::GetString(wc16_string& ret)
{
#ifdef _UNICODE
#error We depend on Unix not being Unicode 
#endif

    // get the string
    const int MAX_STRING = 1024;
    char  buf[MAX_STRING];
    int   len;

    TCOUT.flush();
    len = read( 0, buf, MAX_STRING );

    // TODO:BAM -- mb-enable this!
    if (len < MAX_STRING - 1)
        buf[len] = 0;

    char* psz = tss::strdec( buf, buf + len );
    if ( *psz  == '\n')
        *psz = 0;

    ret = cStringUtil::TstrToWstr( buf );
}


void cTWUtil::GetStringNoEcho(wc16_string& ret)
{
    // set the terminal to no echo mode
    static struct termios Ntty;

    fflush(stdout);
    tcgetattr( 0, &Otty);
    Ntty = Otty;

    Ntty.c_lflag &= ~ECHO;

    // catch SIGINT and SIGQUIT
    old_SIGINT	= tw_signal(SIGINT, RestoreEcho);
    old_SIGQUIT = tw_signal(SIGQUIT, RestoreEcho);

    if (tcsetattr( 0, TCSAFLUSH, &Ntty) != 0 && isatty(0))
    {
        ThrowAndAssert(eTWUtilEchoModeSet());
    }

    GetString(ret);
    
    tcsetattr( 0, TCSAFLUSH, &Otty);
    tw_signal(SIGINT, old_SIGINT);
    tw_signal(SIGQUIT, old_SIGQUIT);
}
#endif

///////////////////////////////////////////////////////////////////////////////
// ParseObjectList
///////////////////////////////////////////////////////////////////////////////
void cTWUtil::ParseObjectList( cTWUtil::GenreObjList& listOut, const cTWUtil::ObjList& listIn )
{

	cGenre::Genre curGenre = cGenreSwitcher::GetInstance()->GetDefaultGenre();

	ASSERT( listIn.size() > 0 );
	listOut.clear();
	GenreObjList::iterator	curIter		= listOut.end();
	cGenre::Genre			iterGenre	= cGenre::GENRE_INVALID;

	// iterate over all of the input...
	//
	for( ObjList::const_iterator i = listIn.begin(); i != listIn.end(); i++ )
	{
		// first, try to interperate the current string as a genre name...
		// 17 Mar 99 mdb -- we now only do this if the string ends in a ':'
		//
		cGenre::Genre g = cGenre::GENRE_INVALID;
		
		if( i->at( i->length()-1 ) == _T(':') )
		{
			TSTRING genreStr;
			genreStr.assign( i->begin(), i->end()-1 );
			g = cGenreSwitcher::GetInstance()->StringToGenre( genreStr.c_str() );
            
	        //
	        // if it is not a valid genre name, then test to see if it could 
            // be an fconame.  If it is not, then it is a badly formed genre.
	        //
		    if( g == cGenre::GENRE_INVALID )
            {
	            std::auto_ptr<iParserGenreUtil> pParseUtil (iTWFactory::GetInstance()->CreateParserGenreUtil());
	            if( ! pParseUtil->IsAbsolutePath( *i ) )
		            throw eTWUnknownSectionName( *i );
            }
		}
		if( g == cGenre::GENRE_INVALID )
		{
			// assume that we are in the correct genre; this is a fully qualified object name
			// TODO -- throw here if cGenreParserHelper says it isn't a fully qualified name.
			//
			if( iterGenre != curGenre )
			{
				// seek to right list; create it if it is not there...
				//
				for( curIter = listOut.begin(); curIter != listOut.end(); curIter++ )
				{
					if( curIter->first == curGenre )
						break;
				}
				if( curIter == listOut.end() )
				{
					// it doesn't exist; we will have to create a new one.
					//
					listOut.push_back( GenreObjs() );
					listOut.back().first = curGenre;
					curIter = listOut.end() - 1;
				}

				iterGenre = curGenre;
			}
			ASSERT( curIter != listOut.end() );
			//
			// add this to the list; assert that it has not been added yet.
			//
			ObjList::iterator oi;
			for( oi = curIter->second.begin(); oi != curIter->second.end(); oi++ )
			{
				if( *oi == *i )
				{
					//TODO -- what should I do here? probably warn and continue...
					ASSERT( false );
					break;
				}
			}
			if( oi == curIter->second.end() )
				curIter->second.push_back( *i );
		}
		else
		{
			// set the current genre to this and continue...
			//
			curGenre = g;
		}
	}

}


///////////////////////////////////////////////////////////////////////////////
// ParseObjectName
///////////////////////////////////////////////////////////////////////////////
cFCOName cTWUtil::ParseObjectName( const TSTRING& fcoName )
{
	std::auto_ptr<iParserGenreUtil> pParseUtil (iTWFactory::GetInstance()->CreateParserGenreUtil());
	cFCOName name( iTWFactory::GetInstance()->GetNameInfo() );
	//
	// make sure the fco name is a full path...
	//
	if( ! pParseUtil->IsAbsolutePath( fcoName ) )
		throw eTWUtilNotFullPath( fcoName );
	//
	// construct the list that InterpretFCOName needs....
	std::list<TSTRING>	inputNameList;
	//
	// dice up the string into a list
	// currently, we only slice it up based on "|" (for nt registry entries)
	//
    TSTRING::size_type pos = fcoName.find_first_of( _T('|') );
	if( pos != TSTRING::npos )
	{
		// if the input string is "foo|bar" then we want the list to 
		// look like this: "foo", "|", "bar" (three entries)
		//
		TSTRING str;
		str.assign( fcoName, 0, pos );
		inputNameList.push_back( str );
		inputNameList.push_back( _T("|") );
		str.assign( fcoName, pos+1, fcoName.length() - pos );
		inputNameList.push_back( str );
	}
	else
	{
		inputNameList.push_back( fcoName );
	}

	pParseUtil->InterpretFCOName( inputNameList, name ) ;
	
	return name;	
}


///////////////////////////////////////////////////////////////////////////////
// Database and report header helpers
///////////////////////////////////////////////////////////////////////////////

TSTRING cTWUtil::GetSystemName()
{
    TSTRING ret;
    iFSServices::GetInstance()->GetMachineName(ret);
    return ret;
}

TSTRING cTWUtil::GetIPAddress()
{
    uint32 ipaddress;
    if (iFSServices::GetInstance()->GetIPAddress(ipaddress) == false)
        return TSS_GetString( cTW, tw::STR_IP_UNKNOWN);
   
    // convert to host byte-order
    ipaddress = tw_ntohl( ipaddress );

    TOSTRINGSTREAM ret;
    ret << (int)(ipaddress >> 24)           << _T(".") 
        << (int)((ipaddress >> 16) & 0xff)  << _T(".") 
        << (int)((ipaddress >> 8) & 0xff)   << _T(".") 
        << (int)(ipaddress & 0xff);

    return ret.str();
}

TSTRING cTWUtil::GetCurrentUser()
{
    TSTRING ret;
    iFSServices::GetInstance()->GetCurrentUserName(ret);
    return ret;
}

TSTRING cTWUtil::GetHostID()
{
    TSTRING ret;
    iFSServices::GetInstance()->GetHostID(ret);
    return ret;
}

///////////////////////////////////////////////////////////////////////////////
// PrintErrorMsg
///////////////////////////////////////////////////////////////////////////////
void cTWUtil::PrintErrorMsg(const eError& e, const TSTRING& strExtra)
{
    ASSERT( e.GetID() != 0 );  // NOTE: BAM  5/9/99 -- there should no longer be an ID of ZERO

    /* BAM 5/9/99 -- this is old stuff
	if((e.GetID() == 0) && (e.GetMsg().empty()))
	{
		// this should only occur at the top level of a program (ie -- twcmdline.cpp) and
		// indicates that an error occurred and an error message has already been printed out.
		// Therefore, we will do nothing here but return.
		return;
	}
    */

	cErrorReporter::PrintErrorMsg(e, strExtra);
}

///////////////////////////////////////////////////////////////////////////////
// ConfirmYN(const TCHAR* prompt)
//
// prompt the user with given string and wait for a 'Y' or 'N' response.
// returns true if 'Y' entered.
///////////////////////////////////////////////////////////////////////////////
bool cTWUtil::ConfirmYN(const TCHAR* prompt)
{
    wc16_string reply;
    TSTRING s;
    int x;

    while (1) 
	{
        TCOUT << prompt;
		GetString(reply);
		
		cStringUtil::Convert(s, reply);
		
#ifdef UNICODE
		for (x = 0; s[x] && iswctype(s[x], wctype("space") ); x++)
			;
#else
		for (x = 0; s[x] && std::isspace<TCHAR>( s[x], std::locale() ); x++)
			;
#endif
			
		if (_totupper(s[x]) == _T('Y'))
			return true;
		else if (_totupper(s[x]) == _T('N'))
			return false;
    }
}

///////////////////////////////////////////////////////////////////////////////
// VerifySiteKey
///////////////////////////////////////////////////////////////////////////////
bool cTWUtil::VerifyCfgSiteKey( const TSTRING& strConfigFile, const TSTRING& siteKeyPath )
{
	// open the config file
	//
	cMemoryArchive	memArch;
    try
    {
	    TSTRING	dummyString;	
        cTWUtil::ReadConfigText( strConfigFile.c_str(), dummyString, &memArch );
    }
    catch (eArchive& e)
    {
        // if the file is corrupted, eArchive may be thrown.
        // For sanity sake we need to re-throw this as eTWUtil
        TSTRING estr;
        estr.assign(TSS_GetString( cCore, core::STR_ERROR_FILENAME ));
        estr.append(strConfigFile);
        estr.append(TSS_GetString( cCore, core::STR_NEWLINE ));
        estr.append(e.GetMsg());

        throw eTWUtilCorruptedFile( estr );
    }

    // only do the test if there is baggage (indicating the cfg file is encrypted)
	//
    ASSERT(memArch.Length() >= 0);
	if (memArch.Length() <= 0)
        return false;

    // get site public key
	//
	cKeyFile siteKeyfile;
	cTWUtil::OpenKeyFile( siteKeyfile, siteKeyPath );

	// create the two public keys...
	//
	memArch.Seek( 0, cBidirArchive::BEGINNING );
	cElGamalSigPublicKey pubKey( memArch.GetMemory() );

	// compare the two ....
	//
	if( ! pubKey.IsEqual( *siteKeyfile.GetPublicKey() ) )
	{
        TSTRING estr;
        estr.assign(TSS_GetString( cTW, tw::STR_ERR2_CFG_KEY_MISMATCH1 ));
        estr.append(strConfigFile);
        estr.append(TSS_GetString( cTW, tw::STR_ERR2_CFG_KEY_MISMATCH2 ));
        estr.append(siteKeyPath);
        estr.append(TSS_GetString( cTW, tw::STR_ERR2_CFG_KEY_MISMATCH3 ));

		throw eTWUtilCfgKeyMismatch( estr );
	}

    return true;
}


