//
// 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.
//
///////////////////////////////////////////////////////////////////////////////
// twprintcmdline.cpp
//
#include "stdtwprint.h"
#include "twprintcmdline.h"
#include "twprintstrings.h"		// strings specific to twprint
#include "twprintcmdline.h"		// functions for the cmd line
#include "twprinterrors.h"

#include "core/cmdlineparser.h"
#include "core/errorgeneral.h"
#include "core/errorbucketimpl.h"	// for the error table
#include "core/usernotify.h"			// for notifying the user of even
#include "core/serializerimpl.h"		// cSerializerImpl
#include "core/archive.h"			// cArchive and friends

#include "tw/configfile.h"
#include "tw/fcodatabasefile.h" // cFCODatabaseFile
#include "tw/fcoreport.h"			// for reports
#include "tw/textdbviewer.h"
#include "tw/textreportviewer.h"	// text report viewer
#include "tw/twutil.h"
#include "tw/headerinfo.h"
#include "tw/systeminfo.h"
#include "tw/twerrors.h"
#include "tw/dbdatasource.h"
#include "tw/twstrings.h"

#include "twcrypto/keyfile.h"			// cKeyFile -- used for encryption

#include "util/fileutil.h"

#include "fco/fcogenre.h"
#include "fco/genreswitcher.h"
#include "fco/twfactory.h"
#include "fco/fcospeclist.h"		// cFCOSpecList
#include "fco/fcopropdisplayer.h"
#include <set>

///////////////////////////////////////////////////////////////////////////////
// Static functions --
///////////////////////////////////////////////////////////////////////////////
static void InitCmdLineCommon(cCmdLineParser& parser);
	// A method for initializing the command line parser with arguments that
	// are common to all modes of twprint.

//#############################################################################
// cTWPrintCmdLine
//#############################################################################


// this is used to make required condition checking in the Init() functions a little more compact
#define TEST_INIT_REQUIREMENT(t, n)\
	if(! t)\
	{\
		TCERR << TSS_GetString(cTW, n) << std::endl;\
		return false;\
	}

// TODO: get Matt to update this with changes he made to tripwire

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineCommon -- 
//			Initialize the cmdlineparser with the arguments that are common
//			to all modes.  Other parameters must be added in the 
//			InitCmdLineParser method of the derived mode.
///////////////////////////////////////////////////////////////////////////////
static void InitCmdLineCommon(cCmdLineParser& parser)
{
    // help
    parser.AddArg(cTWPrintCmdLine::HELP,			TSTRING(_T("?")),	TSTRING(_T("help")),			cCmdLineParser::PARAM_NONE);

    // mode
	parser.AddArg(cTWPrintCmdLine::MODE,			TSTRING(_T("m")),	TSTRING(_T("")),				cCmdLineParser::PARAM_ONE);
	parser.AddArg(cTWPrintCmdLine::MODE_DBPRINT,	TSTRING(_T("")),	TSTRING(_T("print-dbfile")),	cCmdLineParser::PARAM_NONE);
	parser.AddArg(cTWPrintCmdLine::MODE_REPORTPRINT,TSTRING(_T("")),	TSTRING(_T("print-report")),	cCmdLineParser::PARAM_NONE);

    // reporting
	parser.AddArg(cTWPrintCmdLine::VERBOSE,			TSTRING(_T("v")),	TSTRING(_T("verbose")),			cCmdLineParser::PARAM_NONE);
	parser.AddArg(cTWPrintCmdLine::SILENT,			TSTRING(_T("s")),	TSTRING(_T("silent")),			cCmdLineParser::PARAM_NONE);
	parser.AddArg(cTWPrintCmdLine::SILENT,			TSTRING(_T("")),	TSTRING(_T("quiet")),			cCmdLineParser::PARAM_NONE);

    // config file overrides
	parser.AddArg(cTWPrintCmdLine::CFG_FILE,		TSTRING(_T("c")),	TSTRING(_T("cfgfile")),			cCmdLineParser::PARAM_ONE);
	parser.AddArg(cTWPrintCmdLine::LOCAL_KEY_FILE,	TSTRING(_T("L")),	TSTRING(_T("local-keyfile")),	cCmdLineParser::PARAM_ONE);
	parser.AddArg(cTWPrintCmdLine::SITE_KEY_FILE,	TSTRING(_T("S")),	TSTRING(_T("site-keyfile")),	cCmdLineParser::PARAM_ONE);
    
    // unattended operation
	parser.AddArg(cTWPrintCmdLine::PASSPHRASE,		TSTRING(_T("P")),	TSTRING(_T("passphrase")),		cCmdLineParser::PARAM_ONE);

	// the paramters to the command line ... for now, this will take "many", even though in some
	// modes, this is not valid to do...

	// NOTE -- print report doesn't take any naked parameters, and neither does print database (we took that
	//		functionality out) so I changed this to PARAM_NONE.

	// NOTE -- We __DO__ take "naked" parameters in print-database mode, contrary to the above comment!!! It is an error
	// to add this argument here, in the "common" code between the two modes of twprint.  I'm moving the PARAMS arg to 
	// twprint --print-database's "personal" initialization (below). -DRA 8/9/99

	//parser.AddArg(cTWPrintCmdLine::PARAMS,			TSTRING(_T("")),	TSTRING(_T("")),				cCmdLineParser::/*PARAM_NONE*/PARAM_MANY);
		// Error for print-report!!!!

	parser.AddMutEx(cTWPrintCmdLine::VERBOSE, cTWPrintCmdLine::SILENT);
}

///////////////////////////////////////////////////////////////////////////////
// GetMode -- processes the command line arguments and creates an appropriate 
//		structure for the selected mode, or NULL if an error occurs
///////////////////////////////////////////////////////////////////////////////
iTWMode* cTWPrintCmdLine::GetMode( int argc, const TCHAR* const * argv )
{
	// note -- it is assumed the executable name is the first parameter
	if(argc < 2)
	{
        TCOUT << TSS_GetString(cTWPrint, twprint::STR_TWPRINT_VERSION) << std::endl;
		TCOUT << TSS_GetString(cTW, tw::STR_VERSION) << std::endl;
        TCOUT << TSS_GetString(cTW, tw::STR_GET_HELP) << std::endl;
		return NULL;
	}

	int mode = MODE;
	const TCHAR* pcMode;
	if(_tcscmp(argv[1], _T("-m")) == 0)
	{
		if(argc < 3)
		{
            TCOUT << TSS_GetString(cTWPrint, twprint::STR_TWPRINT_VERSION) << std::endl;
            TCOUT << TSS_GetString(cTW, tw::STR_VERSION) << std::endl;
			TCOUT << TSS_GetString(cTW, tw::STR_ERR_NO_MODE) << std::endl;
            TCOUT << TSS_GetString(cTW, tw::STR_GET_HELP) << std::endl;
			return NULL;
		}
		pcMode = argv[2];
		if		(_tcscmp(argv[2], _T("d")) == 0)
			mode = MODE_DBPRINT;
		else if	(_tcscmp(argv[2], _T("r")) == 0)
			mode = MODE_REPORTPRINT;
	}
	else
	{
		pcMode = argv[1];
		if      (_tcscmp(argv[1], _T("--print-dbfile")) == 0)
			mode = MODE_DBPRINT;
		else if	(_tcscmp(argv[1], _T("--print-report")) == 0)
			mode = MODE_REPORTPRINT;
		else if	(_tcscmp(argv[1], _T("--version")) == 0)
		{
		    TCOUT << TSS_GetString(cTW, tw::STR_VERSION) << std::endl;
			return NULL;
		}
        else if (_tcscmp(argv[1], _T("--help")) == 0 || _tcscmp(argv[1], _T("-?")) == 0)
			mode = MODE_HELP;
	}
	
	if(mode == MODE)
	{
		// unknown mode switch
		cDebug d("cTWPrintCmdLine::GetMode");
		d.TraceError(_T("Error: Bad mode switch: %s\n"), pcMode);
		TCERR << TSS_GetString(cTW, tw::STR_UNKOWN_MODE_SPECIFIED) << pcMode << std::endl;
        TCERR << TSS_GetString(cTW, tw::STR_GET_HELP) << std::endl;
		return NULL;
	}


	iTWMode* pRtn = 0;
	switch(mode)
	{
	case cTWPrintCmdLine::MODE_DBPRINT:
		pRtn = new cTWPrintDBMode;
		break;
	case cTWPrintCmdLine::MODE_REPORTPRINT:
		pRtn = new cTWPrintReportMode;
		break;
	case cTWPrintCmdLine::MODE_HELP:
		pRtn = new cTWPrintHelpMode;
		break;
	default:
		ASSERT(false);
	}

	return pRtn;
}

//#############################################################################
// cTWPrintModeCommon -- contains data common to all modes; all cTWMode*_i will
//		derive from this class.
//#############################################################################
class cTWPrintModeCommon
{
public:
	int			mVerbosity;		// must be 0 <= n <= 2
	std::string	mPassPhrase;	// pass phrase for private key
	TSTRING		mLocalKeyFile;
    TSTRING     mSiteKeyFile;
    TSTRING     mConfigFilePath;

	cTWPrintModeCommon() : mVerbosity(1) {}
	cTextReportViewer::ReportingLevel	mReportLevel;	// The level of reporting to use.
};

///////////////////////////////////////////////////////////////////////////////
// FillOutCommonConfigInfo -- fills out all the common info with config file information
///////////////////////////////////////////////////////////////////////////////
static void FillOutCommonConfigInfo(cTWPrintModeCommon* pModeInfo, const cConfigFile& cf)
{
	TSTRING str;
	if(cf.Lookup(TSTRING(_T("LOCALKEYFILE")), str))
		pModeInfo->mLocalKeyFile = str;

	if(cf.Lookup(TSTRING(_T("SITEKEYFILE")), str))
		pModeInfo->mSiteKeyFile = str;

    // 
	// turn all of the file names into full paths (they're relative to the exe dir)
	// 
	TSTRING fullPath;
	if(iFSServices::GetInstance()->FullPath( fullPath, pModeInfo->mLocalKeyFile, cSystemInfo::GetExeDir() ))
		pModeInfo->mLocalKeyFile = fullPath;
}

///////////////////////////////////////////////////////////////////////////////
// FillOutCmdLineInfo -- fills out info common to all modes that appears on the 
//		command line.
///////////////////////////////////////////////////////////////////////////////
static void FillOutCmdLineInfo(cTWPrintModeCommon* pModeInfo, const cCmdLineParser& cmdLine)
{
	cCmdLineIter iter(cmdLine);
	for(iter.SeekBegin(); ! iter.Done(); iter.Next())
	{
		switch(iter.ArgId())
		{
		    case cTWPrintCmdLine::VERBOSE:
			    pModeInfo->mVerbosity = 2;
			break;
		    case cTWPrintCmdLine::SILENT:
			    pModeInfo->mVerbosity = 0;
			break;
		    case cTWPrintCmdLine::LOCAL_KEY_FILE:
			    ASSERT(iter.NumParams() > 0);	// should be caught by cmd line parser
			    pModeInfo->mLocalKeyFile = iter.ParamAt(0);
			break;
		    case cTWPrintCmdLine::SITE_KEY_FILE:
			    ASSERT(iter.NumParams() > 0);	// should be caught by cmd line parser
			    pModeInfo->mSiteKeyFile = iter.ParamAt(0);
			break;
		    case cTWPrintCmdLine::PASSPHRASE:
			{
				// this bites! I have to make sure it is a narrow char string
				ASSERT(iter.NumParams() > 0);	// should be caught by cmd line parser
#ifdef _UNICODE
				char* buf = new char[2*(wcslen(iter.ParamAt(0).c_str()) + 1)];
				wcstombs(buf, iter.ParamAt(0).c_str(), sizeof(buf));
				pModeInfo->mPassPhrase = buf;
				delete [] buf;
#else
				pModeInfo->mPassPhrase = iter.ParamAt(0);
#endif
			}
			break;
			case cTWPrintCmdLine::REPORTLEVEL:
				{
					if ( iter.ParamAt(0) == _T("0"))
						pModeInfo->mReportLevel = cTextReportViewer::SINGLE_LINE;
					else if( iter.ParamAt(0) == _T("1") )
						pModeInfo->mReportLevel = cTextReportViewer::PARSEABLE;
					else if( iter.ParamAt(0) == _T("2") )
						pModeInfo->mReportLevel = cTextReportViewer::SUMMARY_ONLY;
					else if( iter.ParamAt(0) == _T("3") )
						pModeInfo->mReportLevel = cTextReportViewer::CONCISE_REPORT;
					else if( iter.ParamAt(0) == _T("4") )
						pModeInfo->mReportLevel = cTextReportViewer::FULL_REPORT;
					else
					{
						// They specified an illegal level, error.
						TSTRING errStr = _T("Invalid Level: ");
						errStr += iter.ParamAt(0);
						throw eTWPrintInvalidReportLevel( errStr );
					}
				}
				//done with report-level stuff.
				break;
			default:
				break;
		}
	}

	// use the verbosity information
	ASSERT((pModeInfo->mVerbosity >= 0) && (pModeInfo->mVerbosity < 3));
	iUserNotify::GetInstance()->SetVerboseLevel(pModeInfo->mVerbosity);
}

//#############################################################################
// cTWPrintReportMode
//#############################################################################
class cTWPrintReportMode_i : public cTWPrintModeCommon
{
public:    
	TSTRING		mReportFile;

	// ctor can set up some default values
	cTWPrintReportMode_i() : cTWPrintModeCommon(){}
};

void cTWPrintReportMode::SetConfigFile( TSTRING configFilePath )
{
    mpData->mConfigFilePath = configFilePath;
}

///////////////////////////////////////////////////////////////////////////////
// FillOutReportModeConfigInfo -- fills out all the common info with config file information
///////////////////////////////////////////////////////////////////////////////
void cTWPrintReportMode::FillOutReportModeConfigInfo(cTWPrintReportMode_i* pModeInfo, const cConfigFile& cf)
{
	TSTRING str;
	if(cf.Lookup(TSTRING(_T("REPORTFILE")), str))
		pModeInfo->mReportFile = str;
    
	// Find out what level of reporting we should use, use default level if none
	// has been specified.
	if(cf.Lookup(TSTRING(_T("REPORTLEVEL")), str))
    {
        if (_tcsicmp(str.c_str(), _T("0")) == 0)
			/*throw eTWPrintReportLevelZeroCfg( TSS_GetString( cTWPrint, twprint::STR_ERR2_REPORT_LEVEL_ZERO_CFG ) );
		// We error if reporting-level 0 is specified in the config. file
		// Level zero must be specified on the command line. - changed, 7/14/99 dra */
			pModeInfo->mReportLevel = cTextReportViewer::SINGLE_LINE;
        else if (_tcsicmp(str.c_str(), _T("1")) == 0)
            pModeInfo->mReportLevel = cTextReportViewer::PARSEABLE;
        else if (_tcsicmp(str.c_str(), _T("2")) == 0)
            pModeInfo->mReportLevel = cTextReportViewer::SUMMARY_ONLY;
        else if (_tcsicmp(str.c_str(), _T("3")) == 0)
            pModeInfo->mReportLevel = cTextReportViewer::CONCISE_REPORT;
        else if (_tcsicmp(str.c_str(), _T("4")) == 0)
            pModeInfo->mReportLevel = cTextReportViewer::FULL_REPORT;
        else
		{	
			// They specified an illegal level, error.
			TSTRING errStr = _T("Invalid Level: ");
			errStr += str;
			throw eTWPrintInvalidReportLevelCfg( errStr );
		}
    }
	else
		// Use the default level of reporting, they specified none in configuration file.
		pModeInfo->mReportLevel = cTextReportViewer::CONCISE_REPORT;

    // 
	// turn all of the file names into full paths (they're relative to the exe dir)
	// 
	TSTRING fullPath;
	if(iFSServices::GetInstance()->FullPath( fullPath, pModeInfo->mReportFile, cSystemInfo::GetExeDir() ))
		pModeInfo->mReportFile = fullPath;
}

///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWPrintReportMode::cTWPrintReportMode()
{
	mpData = new cTWPrintReportMode_i;
}

cTWPrintReportMode::~cTWPrintReportMode()
{
	delete mpData;
}

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWPrintReportMode::InitCmdLineParser(cCmdLineParser& parser)
{
	InitCmdLineCommon( parser );

	parser.AddArg(cTWPrintCmdLine::REPORT_FILE,		TSTRING(_T("r")),	TSTRING(_T("twrfile")),		    cCmdLineParser::PARAM_ONE);

	// multiple levels of reporting
	parser.AddArg(cTWPrintCmdLine::REPORTLEVEL,		TSTRING(_T("t")),	TSTRING(_T("report-level")),	cCmdLineParser::PARAM_ONE);
}

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWPrintReportMode::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
	// first, fill out everything with the config file info...
	FillOutCommonConfigInfo(mpData, cf);
    FillOutReportModeConfigInfo(mpData, cf);
	
	// TODO -- should I error or warn for (1) mutual exclustion errors or (2) unneeded cmd line
	//		parameters? I think I should, but I won't right now.
	// TODO -- error at the end of Init() if I am missing any requires parameter values..

	// now, parse the command line...
	// this takes care of the common stuff...
	FillOutCmdLineInfo(mpData, cmdLine);

	// now do the stuff specific to this mode...
	cCmdLineIter iter(cmdLine);
	for(iter.SeekBegin(); ! iter.Done(); iter.Next())
	{
		switch(iter.ArgId())
		{
		    case cTWPrintCmdLine::REPORT_FILE:
			    ASSERT(iter.NumParams() > 0);	// should be caught by cmd line parser
			    mpData->mReportFile = iter.ParamAt(0);
			break;
			case cTWPrintCmdLine::REPORTLEVEL:
				{
					if ( iter.ParamAt(0) == _T("0"))
						mpData->mReportLevel = cTextReportViewer::SINGLE_LINE;
					else if( iter.ParamAt(0) == _T("1") )
						mpData->mReportLevel = cTextReportViewer::PARSEABLE;
					else if( iter.ParamAt(0) == _T("2") )
						mpData->mReportLevel = cTextReportViewer::SUMMARY_ONLY;
					else if( iter.ParamAt(0) == _T("3") )
						mpData->mReportLevel = cTextReportViewer::CONCISE_REPORT;
					else if( iter.ParamAt(0) == _T("4") )
						mpData->mReportLevel = cTextReportViewer::FULL_REPORT;
					else
					{
						// They specified an illegal level, error.
						TSTRING errStr = _T("Invalid Level: ");
						errStr += iter.ParamAt(0);
						throw eTWPrintInvalidReportLevel( errStr );
					}
				}
				//done with report-level stuff.
			break;
			default:
				// should I do anything, or just ignore this?
				;
		}
	}
    
	//----------------------------------------
	// I require the following information:
	//
	// * local key file
	// * report file
	//-----------------------------------------
	TEST_INIT_REQUIREMENT((! mpData->mLocalKeyFile.empty()),    tw::STR_ERR_MISSING_LOCAL_KEY);
	TEST_INIT_REQUIREMENT((! mpData->mReportFile.empty()),		tw::STR_ERR_MISSING_REPORT);

	// check that the config file and site key file are in sync...
	//
	if( ! mpData->mConfigFilePath.empty() )
        try 
        {
		    if (cTWUtil::VerifyCfgSiteKey( mpData->mConfigFilePath, mpData->mSiteKeyFile ) == false)
                cTWUtil::PrintErrorMsg(eTWCfgUnencrypted(_T(""), eError::NON_FATAL|eError::SUPRESS_THIRD_MSG));
        }
        catch (eTWUtilCfgKeyMismatch& e)        
        {
            e.SetFatality(false);
            cTWUtil::PrintErrorMsg(e);
        }

	return true;
}

///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWPrintReportMode::Execute(cErrorQueue* pQueue)
{
	cFCOReport			report;
	cFCOReportHeader	reportHeader;

	try
	{
        ASSERT( ! mpData->mReportFile.empty() );
		// make sure the report file exists...
		cFileUtil::TestFileExists(mpData->mReportFile);

        cKeyFile	            localKeyfile;
        const cElGamalSigPublicKey*    pKey;
		cFCOReport              report;

	    cTWUtil::OpenKeyFile( localKeyfile, mpData->mLocalKeyFile );
	    pKey = localKeyfile.GetPublicKey();

        if( !cTWUtil::IsObjectEncrypted( mpData->mReportFile.c_str(), cFCOReport::GetFileHeaderID(), TSS_GetString( cTW, tw::STR_ERR_REPORT_READ ) ) )
        {
	    	// note (rather than warn) if the database is not encrytped
	    	iUserNotify::GetInstance()->Notify(iUserNotify::V_NORMAL, TSS_GetString(cTW, tw::STR_REPORT_NOT_ENCRYPTED).c_str());
		}

		// open the report...
        bool bDummy;
        cTWUtil::ReadReport( mpData->mReportFile.c_str(), reportHeader, report, pKey, false, bDummy );		

        // print it
        cTextReportViewer trv( reportHeader, report );
        trv.PrintTextReport( _T("-"), mpData->mReportLevel );
	}
	catch(eError& e)
	{
		cTWUtil::PrintErrorMsg(e);
		return 1;
	}

	return 0;
}

///////////////////////////////////////////////////////////////////////////////
// GetModeUsage -- returns a mode-specific usage statement.
///////////////////////////////////////////////////////////////////////////////
TSTRING cTWPrintReportMode::GetModeUsage() 
{
	return TSS_GetString(cTWPrint, twprint::STR_TWPRINT_HELP_PRINT_REPORT ); 
}

//#############################################################################
// cTWPrintDBMode
//#############################################################################
class cTWPrintDBMode_i : public cTWPrintModeCommon
{
public:
	TSTRING		mDbFile;
	std::vector<TSTRING> mFilesToCheck;

	// ctor can set up some default values
	cTWPrintDBMode_i() : cTWPrintModeCommon() {}
};

///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWPrintDBMode::cTWPrintDBMode()
{
	mpData = new cTWPrintDBMode_i;
}

cTWPrintDBMode::~cTWPrintDBMode()
{
	delete mpData;
}

void cTWPrintDBMode::SetConfigFile( TSTRING configFilePath )
{
    mpData->mConfigFilePath = configFilePath;
}

///////////////////////////////////////////////////////////////////////////////
// FillOutDBModeConfigInfo -- fills out all the common info with config file information
///////////////////////////////////////////////////////////////////////////////
void cTWPrintDBMode::FillOutDBModeConfigInfo(cTWPrintDBMode_i* pModeInfo, const cConfigFile& cf)
{
	TSTRING str;
	if(cf.Lookup(TSTRING(_T("DBFILE")), str))
		pModeInfo->mDbFile = str;
    
    // 
	// turn all of the file names into full paths (they're relative to the exe dir)
	// 
	TSTRING fullPath;
	if(iFSServices::GetInstance()->FullPath( fullPath, pModeInfo->mDbFile, cSystemInfo::GetExeDir() ))
		pModeInfo->mDbFile = fullPath;
}

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWPrintDBMode::InitCmdLineParser( cCmdLineParser& parser )
{
	InitCmdLineCommon( parser );

	parser.AddArg(cTWPrintCmdLine::DB_FILE,			TSTRING(_T("d")),	TSTRING(_T("dbfile")),			cCmdLineParser::PARAM_ONE);

	// For the variable object list.
	parser.AddArg(cTWPrintCmdLine::PARAMS,			TSTRING(_T("")),	TSTRING(_T("")),				cCmdLineParser::PARAM_MANY);

}

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWPrintDBMode::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
	// first, fill out everything with the config file info...
	FillOutCommonConfigInfo(mpData, cf);
    FillOutDBModeConfigInfo(mpData, cf);
	
	// TODO -- should I error or warn for (1) mutual exclustion errors or (2) unneeded cmd line
	//		parameters? I think I should, but I won't right now.
	// TODO -- error at the end of Init() if I am missing any requires parameter values..

	// now, parse the command line...
	// this takes care of the common stuff...
	FillOutCmdLineInfo(mpData, cmdLine);

	// now do the stuff specific to this mode...
	cCmdLineIter iter(cmdLine);
	for(iter.SeekBegin(); ! iter.Done(); iter.Next())
	{
		switch(iter.ArgId())
		{
		    case cTWPrintCmdLine::DB_FILE:
			    ASSERT(iter.NumParams() > 0);	// should be caught by cmd line parser
			    mpData->mDbFile = iter.ParamAt(0);
			break;
			case cTWPrintCmdLine::PARAMS:
				{
					// pack all of these onto the files to check list...
					mpData->mFilesToCheck.clear();
					for(int i=0; i < iter.NumParams(); i++)
					{
						mpData->mFilesToCheck.push_back(iter.ParamAt(i));
					}
				}
			default:
				// TODO: should I do anything, or just ignore this?
				;
		}
	}
    
    //----------------------------------------
	// I require the following information:
	//
	// * local key file
	// * report file
	//-----------------------------------------
	TEST_INIT_REQUIREMENT((! mpData->mLocalKeyFile.empty()),	tw::STR_ERR_MISSING_LOCAL_KEY);
	TEST_INIT_REQUIREMENT((! mpData->mDbFile.empty()),		    tw::STR_ERR_MISSING_DB);

	// check that the config file and site key file are in sync...
	//
	if( ! mpData->mConfigFilePath.empty() )
        try 
        {
		    if (cTWUtil::VerifyCfgSiteKey( mpData->mConfigFilePath, mpData->mSiteKeyFile ) == false)
                cTWUtil::PrintErrorMsg(eTWCfgUnencrypted(_T(""), eError::NON_FATAL|eError::SUPRESS_THIRD_MSG));
        }
        catch (eTWUtilCfgKeyMismatch& e)        
        {
            e.SetFatality(false);
            cTWUtil::PrintErrorMsg(e);
        }

	return true;
}

///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWPrintDBMode::Execute(cErrorQueue* pQueue)
{
	cDebug d("cTWPrintDBMode::Execute");
	int ret = 0;

	try
	{
        ASSERT( ! mpData->mDbFile.empty() );
		// make sure the database file exists...
		cFileUtil::TestFileExists(mpData->mDbFile);

		cFCODatabaseFile db;
		cKeyFile		localKeyfile;
		const cElGamalSigPublicKey*	pKey = 0;

	    cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);
	    pKey = localKeyfile.GetPublicKey();

        if( !cTWUtil::IsObjectEncrypted( mpData->mDbFile.c_str(), cFCODatabaseFile::GetFileHeaderID(), TSS_GetString( cTW, tw::STR_ERR_DB_READ ) ) )
	    {
	        // warn if the database is not encrytped
	        if(iUserNotify::GetInstance()->GetVerboseLevel() > iUserNotify::V_SILENT)
	        {
                cTWUtil::PrintErrorMsg(eTWDbNotEncrypted(_T(""), eError::NON_FATAL|eError::SUPRESS_THIRD_MSG));
            }
	    }
		
	    // open the database; note that ReadDatabase will set bEncrypted and open the 
	    // key file if necessary
        bool bDummy;
		cTWUtil::ReadDatabase(mpData->mDbFile.c_str(), db, pKey, bDummy);

		if( mpData->mFilesToCheck.size() > 0 )
		{
			//------------------------------------------------
			// print specific FCOs from the database
			//------------------------------------------------
			cTWUtil::GenreObjList listOut;
			cTWUtil::ParseObjectList( listOut, mpData->mFilesToCheck );

			// now, iterate through the list of objects...
			//
			cFCODatabaseFile::iterator dbIter( db );
			for( cTWUtil::GenreObjList::iterator genreIter = listOut.begin(); genreIter != listOut.end(); genreIter++ )
			{
				dbIter.SeekToGenre( genreIter->first );
				if( ! dbIter.Done() )
				{
					cGenreSwitcher::GetInstance()->SelectGenre( (cGenre::Genre)dbIter.GetGenre() );
					cDbDataSourceIter dsIter( &dbIter.GetDb() );
                    dsIter.SetErrorBucket(pQueue);
                    iFCONameTranslator* pNT = cGenreSwitcher::GetInstance()->GetFactoryForGenre( (cGenre::Genre)dbIter.GetGenre() )->GetNameTranslator(); 
					//
					// iterate over all the objects in this genre....
					//
					for(cTWUtil::ObjList::iterator it = genreIter->second.begin(); it != genreIter->second.end(); it++)
					{
						try
						{
							cFCOName name = cTWUtil::ParseObjectName( *it );
							dsIter.SeekToFCO( name, false );
							if( (! dsIter.Done()) && (dsIter.HasFCOData()) )
							{
								cTextDBViewer::OutputFCO( dsIter, dbIter.GetGenreHeader().GetPropDisplayer(), pNT, &TCOUT );
							}
							else
							{
								eTWUtilObjNotInDb e( *it );
								e.SetFatality( false );
								cTWUtil::PrintErrorMsg( e );
							}
						}
						catch( eError& e)
						{
							e.SetFatality( false );
							cTWUtil::PrintErrorMsg( e );
						}
					}
					
				}
				else
				{
					eTWDbDoesntHaveGenre e( cGenreSwitcher::GetInstance()->GenreToString( (cGenre::Genre)genreIter->first, true ) );
					e.SetFatality( false );
					cTWUtil::PrintErrorMsg( e );
				}
			}

		}
		else
		{
			//------------------------------------------------
			// printing the entire db
			//------------------------------------------------
	        cTextDBViewer::PrintDB( db, _T("-") );
		}

	}
	catch(eError& e)
	{
		cTWUtil::PrintErrorMsg(e);
		return 1;
	}

	return ret;
}

///////////////////////////////////////////////////////////////////////////////
// GetModeUsage -- returns a mode-specific usage statement.
///////////////////////////////////////////////////////////////////////////////
TSTRING cTWPrintDBMode::GetModeUsage() 
{
	return TSS_GetString(cTWPrint, twprint::STR_TWPRINT_HELP_PRINT_DATABASE ); 
}



//#############################################################################
// cTWPrintHelpMode
//#############################################################################

class cTWPrintHelpMode_i
{
public:
	cTWPrintHelpMode_i() {}
	~cTWPrintHelpMode_i() {}

	// A list of modes to output usage statements for:
	std::set< TSTRING > mlModes;
	std::set< TSTRING > mPrinted;
};


///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWPrintHelpMode::cTWPrintHelpMode()
{
	mpData = new cTWPrintHelpMode_i();
}

cTWPrintHelpMode::~cTWPrintHelpMode()
{
	delete mpData;
}

void cTWPrintHelpMode::SetConfigFile( TSTRING configFilePath )
{
}

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWPrintHelpMode::InitCmdLineParser(cCmdLineParser& cmdLine)
{
	// We're only interested in one parameter, that being help.  Anything else
	// passed to this mode should be a cmdlineparser error.
	cmdLine.AddArg(cTWPrintCmdLine::MODE,				TSTRING(_T("m")),	TSTRING(_T("")),				cCmdLineParser::PARAM_MANY, true);
	cmdLine.AddArg(cTWPrintCmdLine::MODE_HELP,			TSTRING(_T("?")),	TSTRING(_T("help")),			cCmdLineParser::PARAM_MANY);
	cmdLine.AddArg(cTWPrintCmdLine::MODE_HELP_ALL,		TSTRING(_T("")),	TSTRING(_T("all")),				cCmdLineParser::PARAM_MANY);
	cmdLine.AddArg(cTWPrintCmdLine::MODE_REPORTPRINT,	TSTRING(_T("r")),	TSTRING(_T("print-report")),	cCmdLineParser::PARAM_MANY);
	cmdLine.AddArg(cTWPrintCmdLine::MODE_DBPRINT,		TSTRING(_T("d")),	TSTRING(_T("print-dbfile")),	cCmdLineParser::PARAM_MANY);
}

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWPrintHelpMode::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
	cCmdLineIter iter(cmdLine);

	// Grab the arguments from the help parameter:
	for(iter.SeekBegin(); ! iter.Done(); iter.Next())
	{
		switch(iter.ArgId())
		{
			case cTWPrintCmdLine::MODE_HELP:
				{
					int i;
					for( i = 0; i < iter.NumParams(); ++i )
						mpData->mlModes.insert( iter.ParamAt(i) );
				}
				break;
			case cTWPrintCmdLine::MODE:
				{
					int i;
					for( i = 0; i < iter.NumParams(); ++i )
					{
						mpData->mlModes.insert( iter.ParamAt(i) );
					}
				}
				break;
			// Begin ugly hack so we can allow users to enter the mode
			// names with "--" prepended.  We have to do this, since
			// the cmdlineparser treats them as switches.
			case cTWPrintCmdLine::MODE_HELP_ALL:	// fall through
			case cTWPrintCmdLine::MODE_REPORTPRINT: // fall through
			case cTWPrintCmdLine::MODE_DBPRINT:
				{
					int i;
					TSTRING str = iter.ActualParam();
					// Kill off the initial "--" or "-"
					str.erase( 0, 1 );
					if( str.length() != 1)
						str.erase( 0, 1 );

					// push back the parameter that was actually passed.
					mpData->mlModes.insert( str );
					// grab all the "parameters" following the mode/switch, since
					// they may be valid modes without the "--" prepended.
					for( i = 0; i < iter.NumParams(); ++i )
						mpData->mlModes.insert( iter.ParamAt(i) );
				}
				break;
			default:
				// should I do anything, or just ignore this?
				;
		}
	}

    return true;
}

///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWPrintHelpMode::Execute(cErrorQueue* pQueue)
{
	cDebug d("cTWPrintHelpMode::Execute");

	// The iterator we will use to traverse the list of arguments:
	std::set< TSTRING >::iterator it = mpData->mlModes.begin();

	// We'll want to output the version information, regardless:
	TCOUT << TSS_GetString(cTWPrint, twprint::STR_TWPRINT_VERSION) << std::endl;
	TCOUT << TSS_GetString( cTW, tw::STR_VERSION) << std::endl;

	if( it == mpData->mlModes.end() ) // all that was passed was --help
	{
		// Output a short usage statement for each mode
		TCOUT << TSS_GetString(cTWPrint, twprint::STR_TWPRINT_USAGE_SUMMARY);
		//
		// That's it.  return
		return 1;
	}

	for( ; it != mpData->mlModes.end(); ++it )
	{
		if( _tcscmp( (*it).c_str(), _T("all")) == 0 )
		{
			//Since --help all was passed, emit all help messages and return.
			TCOUT<< TSS_GetString( cTWPrint, twprint::STR_TWPRINT_HELP_PRINT_REPORT );
			TCOUT<< TSS_GetString( cTWPrint, twprint::STR_TWPRINT_HELP_PRINT_DATABASE );

			//We're done, return
			return 1;
		}
	}
	
	//We need some subset of the usage statements.  Figure out which modes have
	//been specified:
	it = mpData->mlModes.begin();
	for( ; it != mpData->mlModes.end(); ++it )
	{
		if ( _tcscmp( (*it).c_str(), _T("print-report") ) == 0 || _tcscmp( (*it).c_str(), _T("r") ) == 0)
		{
			// make sure we don't print the same help twice...
			if( mpData->mPrinted.find(_T("print-report")) == mpData->mPrinted.end() )
			{
				TCOUT<< TSS_GetString( cTWPrint, twprint::STR_TWPRINT_HELP_PRINT_REPORT );
				mpData->mPrinted.insert( _T("print-report") );
			}
		}
		else if( _tcscmp( (*it).c_str(), _T("print-dbfile") ) == 0 || _tcscmp( (*it).c_str(), _T("d") ) == 0)
		{
			if( mpData->mPrinted.find(_T("print-dbfile")) == mpData->mPrinted.end() )
			{
				TCOUT<< TSS_GetString( cTWPrint, twprint::STR_TWPRINT_HELP_PRINT_DATABASE );
				mpData->mPrinted.insert( _T("print-dbfile") );
			}
		}
		else
		{
			cTWUtil::PrintErrorMsg( eTWPrintInvalidParamHelp( (*it), eError::NON_FATAL ) );
			TCOUT << std::endl;
			// emit error/warning string, this mode does not exist
		}
	}

	//Everything went okay
    return 1;
}

