//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000-2018 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.
//
///////////////////////////////////////////////////////////////////////////////
// twcmdline.cpp
#include "stdtripwire.h"
#include "twcmdline.h"
#include "core/cmdlineparser.h"
#include "tw/configfile.h"

#include "core/serializerimpl.h" // cSerializerImpl
#include "core/archive.h"        // cArchive and friends
#include "fco/fcospeclist.h"     // cFCOSpecList
#include "tripwirestrings.h"     // tripwire.exe specific strings
#include "twcrypto/keyfile.h"    // cKeyFile -- used for encryption
#include "tw/twutil.h"
#include "twcmdlineutil.h"       // utility functions for the cmd line
#include "tw/fcoreport.h"        // for reports
#include "tw/textreportviewer.h" // text report viewer
#include "core/usernotify.h"     // for notifying the user of events
#include "tw/fcoreportutil.h"    // for finalizing report
#include "tw/fcodatabaseutil.h"  // for finalizing database
#include "tw/headerinfo.h"       // for finalizing database
#include "fco/fcopropdisplayer.h"
#include "tw/systeminfo.h"
#include "tw/filemanipulator.h"
#include "fco/twfactory.h"
#include "mailmessage.h" // used for email reporting
#include "fco/genrespeclist.h"
#include "core/twlimits.h" // for severity limits
#include "core/errorgeneral.h"
#include "core/corestrings.h"
#include "tw/twerrors.h"
#include "core/stringutil.h"
#include "util/fileutil.h"
#include "tw/twstrings.h"
#include "syslog_trip.h"
#include <set>
#include "fco/parsergenreutil.h" // this is needed to figure out if a path is fully qualified for the current genre.
#include "tw/fcodatabasefile.h"
#include "fco/signature.h"
#include "fco/genreswitcher.h"
#include "generatedb.h"
#include "integritycheck.h"
#include "updatedb.h"
#include "policyupdate.h"
#include "core/platform.h"

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

#include "fs/fsdatasourceiter.h" // for cross file systems flag
#include <unistd.h>              // for _exit()

//-----------------------------------------------------------------------------
// #defines
//-----------------------------------------------------------------------------

// this is used to make required condition checking in the Init() functions a little more compact
#define TEST_INIT_REQUIREMENT(t, c, n)                                     \
    if (!t)                                                                \
    {                                                                      \
        cTWUtil::PrintErrorMsg(eTWInitialization(TSS_GetString(c, n), 0)); \
        return false;                                                      \
    }


static bool util_GetEditor(TSTRING& strEd);

//#############################################################################
// cTWCmdLine
//#############################################################################


///////////////////////////////////////////////////////////////////////////////
// GetMode -- processes the command line arguments and creates an appropriate
//    structure for the selected mode, or NULL if an error occurs
///////////////////////////////////////////////////////////////////////////////
iTWMode* cTWCmdLine::GetMode(int argc, const TCHAR* const* argv)
{
    // note -- it is assumed the executable name is the first parameter
    if (argc < 2)
    {
        TCOUT << TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_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(cTripwire, tripwire::STR_TRIPWIRE_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("i")) == 0)
            mode = MODE_INIT;
        else if (_tcscmp(argv[2], _T("c")) == 0)
            mode = MODE_CHECK;
        else if (_tcscmp(argv[2], _T("u")) == 0)
            mode = MODE_UPDATE_DB;
        else if (_tcscmp(argv[2], _T("p")) == 0)
            mode = MODE_UPDATE_POL;
        else if (_tcscmp(argv[2], _T("t")) == 0)
            mode = MODE_TEST;
    }
    else
    {
        pcMode = argv[1];
        if (_tcscmp(argv[1], _T("--init")) == 0)
            mode = MODE_INIT;
        else if (_tcscmp(argv[1], _T("--check")) == 0)
            mode = MODE_CHECK;
        else if (_tcscmp(argv[1], _T("--update")) == 0)
            mode = MODE_UPDATE_DB;
        else if (_tcscmp(argv[1], _T("--update-policy")) == 0)
            mode = MODE_UPDATE_POL;
        else if (_tcscmp(argv[1], _T("--version")) == 0)
            mode = MODE_VERSION;
        else if (_tcscmp(argv[1], _T("--help")) == 0 || _tcscmp(argv[1], _T("-?")) == 0)
            mode = MODE_HELP;
        else if (_tcscmp(argv[1], _T("--test")) == 0)
            mode = MODE_TEST;

#ifdef DEBUG
        if (_tcscmp(argv[1], _T("--explore")) == 0)
            mode = MODE_EXPLORE;
        if (_tcscmp(argv[1], _T("--verifydb")) == 0)
            mode = MODE_DEBUG;
#endif
    }

    if (mode == MODE)
    {
        // unknown mode switch
        cDebug d("cTWCmdLine::GetMode");
        d.TraceError(_T("Error: Bad mode switch: %s\n"), pcMode);
        TCERR << TSS_GetString(cTW, tw::STR_UNKNOWN_MODE_SPECIFIED) << pcMode << std::endl
              << TSS_GetString(cTW, tw::STR_GET_HELP) << std::endl;
        return NULL;
    }


    iTWMode* pRtn = 0;
    switch (mode)
    {
    case cTWCmdLine::MODE_INIT:
        pRtn = new cTWModeDbInit;
        break;
    case cTWCmdLine::MODE_CHECK:
        pRtn = new cTWModeIC;
        break;
    case cTWCmdLine::MODE_UPDATE_DB:
        pRtn = new cTWModeDbUpdate;
        break;
    case cTWCmdLine::MODE_UPDATE_POL:
        pRtn = new cTWModePolUpdate;
        break;
    case cTWCmdLine::MODE_TEST:
        pRtn = new cTWModeTest;
        break;
    case cTWCmdLine::MODE_HELP:
        pRtn = new cTWModeHelp;
        break;
    case cTWCmdLine::MODE_VERSION:
        pRtn = new cTWModeVersion;
        break;

//Explore and Debug modes are invisible unless DEBUG is defined.
#ifdef DEBUG
    case cTWCmdLine::MODE_EXPLORE:
        pRtn = new cTWModeExploreDb;
        break;
    case cTWCmdLine::MODE_DEBUG:
        pRtn = new cTWModeDebugDb;
        break;
#endif

    default:
        ASSERT(false);
    }

    return pRtn;
}

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineCommon -- initializes the command line switches common to all
//    modes
///////////////////////////////////////////////////////////////////////////////
static void InitCmdLineCommon(cCmdLineParser& parser)
{
    parser.AddArg(cTWCmdLine::HELP, TSTRING(_T("?")), TSTRING(_T("help")), cCmdLineParser::PARAM_NONE);
    parser.AddArg(cTWCmdLine::MODE, TSTRING(_T("m")), TSTRING(_T("")), cCmdLineParser::PARAM_ONE);
    parser.AddArg(cTWCmdLine::VERBOSE, TSTRING(_T("v")), TSTRING(_T("verbose")), cCmdLineParser::PARAM_NONE);
    parser.AddArg(cTWCmdLine::SILENT, TSTRING(_T("s")), TSTRING(_T("silent")), cCmdLineParser::PARAM_NONE);
    parser.AddArg(cTWCmdLine::SILENT, TSTRING(_T("")), TSTRING(_T("quiet")), cCmdLineParser::PARAM_NONE);
    //parser.AddArg(cTWCmdLine::NO_BACKUP,    TSTRING(_T("B")), TSTRING(_T("nobackup")),      cCmdLineParser::PARAM_NONE);
    // this was dropped from 1.5 -- mdb
    parser.AddArg(
        cTWCmdLine::LOCAL_PASSPHRASE, TSTRING(_T("P")), TSTRING(_T("local-passphrase")), cCmdLineParser::PARAM_ONE);
    parser.AddArg(cTWCmdLine::POL_FILE, TSTRING(_T("p")), TSTRING(_T("polfile")), cCmdLineParser::PARAM_ONE);
    parser.AddArg(cTWCmdLine::CFG_FILE, TSTRING(_T("c")), TSTRING(_T("cfgfile")), cCmdLineParser::PARAM_ONE);
    parser.AddArg(cTWCmdLine::SITE_KEY_FILE, TSTRING(_T("S")), TSTRING(_T("site-keyfile")), cCmdLineParser::PARAM_ONE);
    parser.AddArg(
        cTWCmdLine::LOCAL_KEY_FILE, TSTRING(_T("L")), TSTRING(_T("local-keyfile")), cCmdLineParser::PARAM_ONE);
    parser.AddArg(cTWCmdLine::DB_FILE, TSTRING(_T("d")), TSTRING(_T("dbfile")), cCmdLineParser::PARAM_ONE);
    parser.AddArg(cTWCmdLine::REPORT_FILE, TSTRING(_T("r")), TSTRING(_T("twrfile")), cCmdLineParser::PARAM_ONE);

    //
    // mutual exclusions
    //
    parser.AddMutEx(cTWCmdLine::VERBOSE, cTWCmdLine::SILENT);
}


///////////////////////////////////////////////////////////////////////////////
// Set up temp directory
///////////////////////////////////////////////////////////////////////////////
static void util_InitTempDirectory(const cConfigFile& cf)
{
    TSTRING temp_directory;
    cf.Lookup(TSTRING(_T("TEMPDIRECTORY")), temp_directory);

    if (temp_directory.empty())
    {
#if IS_AROS
        temp_directory = "T:";
#elif IS_DOS_DJGPP
        temp_directory = "/dev/c/temp/";
#elif IS_RISCOS
        temp_directory = "/!BOOT/Resources/!Scrap/ScrapDirs/ScrapDir";
#elif IS_REDOX
        temp_directory = "/file/tmp/";
#else
        temp_directory = "/tmp/";
#endif
    }

#if !IS_RISCOS
    // make sure we have a trailing slash -- thanks Jarno...
    //
    if (*temp_directory.rbegin() != '/')
    {
        temp_directory.push_back('/');
    }
#endif

    // make sure it exists...
    //

#if IS_AROS || IS_RISCOS
    temp_directory = cDevicePath::AsNative(temp_directory);
#elif IS_DOS_DJGPP
    temp_directory = cDevicePath::AsPosix(temp_directory);
#endif

    if (access(temp_directory.c_str(), F_OK) != 0)
    {
        TSTRING errStr = TSS_GetString(cCore, core::STR_BAD_TEMPDIRECTORY);
        TSTRING tmpStr = _T("Directory: ");
        tmpStr += (temp_directory + _T("\n"));
        tmpStr += errStr;
        throw eTWInvalidTempDirectory(tmpStr);
    }
    else
    {
#if IS_RISCOS
        if (*temp_directory.rbegin() != '.')
        {
            temp_directory.push_back('.');
        }
#endif

        iFSServices::GetInstance()->SetTempDirName(temp_directory);
    }
}


///////////////////////////////////////////////////////////////////////////////
// Set up various email reporting options
///////////////////////////////////////////////////////////////////////////////
static void util_InitEmailOptions(cTWModeCommon* pModeInfo, const cConfigFile& cf)
{
    TSTRING str;
    if (cf.Lookup(TSTRING(_T("GLOBALEMAIL")), str))
    {
        if (str.length() != 0)
            pModeInfo->mGlobalEmail = str;
    }

    //
    // Set the report-viewing level if one has been specified, use
    // default level otherwise.
    //
    if (cf.Lookup(TSTRING(_T("EMAILREPORTLEVEL")), str))
    {
        if (_tcsicmp(str.c_str(), _T("0")) == 0)
            pModeInfo->mEmailReportLevel = cTextReportViewer::SINGLE_LINE;
        else if (_tcsicmp(str.c_str(), _T("1")) == 0)
            pModeInfo->mEmailReportLevel = cTextReportViewer::PARSEABLE;
        else if (_tcsicmp(str.c_str(), _T("2")) == 0)
            pModeInfo->mEmailReportLevel = cTextReportViewer::SUMMARY_ONLY;
        else if (_tcsicmp(str.c_str(), _T("3")) == 0)
            pModeInfo->mEmailReportLevel = cTextReportViewer::CONCISE_REPORT;
        else if (_tcsicmp(str.c_str(), _T("4")) == 0)
            pModeInfo->mEmailReportLevel = cTextReportViewer::FULL_REPORT;
        else
        {
            // They specified an illegal level, error.
            TSTRING errStr = _T("Invalid Level: ");
            errStr += str;
            throw eTWInvalidReportLevelCfg(errStr);
        }
    }
    else
    {
        // no level was specified in the configuration file, use default.
        pModeInfo->mEmailReportLevel = cTextReportViewer::CONCISE_REPORT;
    }

    // Decide what mail method should be used to email reports
    if (cf.Lookup(TSTRING(_T("MAILMETHOD")), str))
    {
        if (_tcsicmp(str.c_str(), _T("SENDMAIL")) == 0)
            pModeInfo->mMailMethod = cMailMessage::MAIL_BY_PIPE;
        else if (_tcsicmp(str.c_str(), _T("SMTP")) == 0)
            pModeInfo->mMailMethod = cMailMessage::MAIL_BY_SMTP;
        else
            pModeInfo->mMailMethod = cMailMessage::INVALID_METHOD;
    }
    else
    {
        pModeInfo->mMailMethod = cMailMessage::NO_METHOD;
    }

#if !SUPPORTS_NETWORKING
    if (pModeInfo->mMailMethod == cMailMessage::MAIL_BY_SMTP)
        throw eMailSMTPNotSupported();
#endif

    // Get the SMTP server
    if (cf.Lookup(TSTRING(_T("SMTPHOST")), str))
    {
        pModeInfo->mSmtpHost = str;
    }
    else
    {
        pModeInfo->mSmtpHost = _T("127.0.0.1"); // this is the default
    }

    // Get the SMTP port number
    if (cf.Lookup(TSTRING(_T("SMTPPORT")), str))
    {
        int i = _ttoi(str.c_str());
        if (i < 0 || i > SHRT_MAX)
            throw eTWInvalidPortNumber(str);
        pModeInfo->mSmtpPort = static_cast<unsigned short>(i);
    }
    else
    {
        pModeInfo->mSmtpPort = 25; // this is the default
    }

    // Get the mail program to use if we're piping our email
    if (cf.Lookup(TSTRING(_T("MAILPROGRAM")), str))
    {
        pModeInfo->mMailProgram = str;
    }
    else
    {
        pModeInfo->mMailProgram.erase(); // MAILPROGRAM is not required to be specified
    }

    // Get the mail program to use if we're piping our email
    if (cf.Lookup(TSTRING(_T("MAILNOVIOLATIONS")), str))
    {
        if (_tcsicmp(str.c_str(), _T("true")) == 0)
            pModeInfo->mMailNoViolations = true;
        else
            pModeInfo->mMailNoViolations = false;
    }
    else
    {
        pModeInfo->mMailNoViolations = true; // MAILPROGRAM is not required to be specified
    }

    if (cf.Lookup(TSTRING(_T("MAILFROMADDRESS")), str))
    {
        pModeInfo->mMailFrom = str;
    }
}


///////////////////////////////////////////////////////////////////////////////
// FillOutConfigInfo -- fills out all the common info with config file information
///////////////////////////////////////////////////////////////////////////////
static void FillOutConfigInfo(cTWModeCommon* pModeInfo, const cConfigFile& cf)
{
    TSTRING str;
    if (cf.Lookup(TSTRING(_T("POLFILE")), str))
    {
        pModeInfo->mPolFile = str;
    }
    if (cf.Lookup(TSTRING(_T("DBFILE")), str))
    {
        pModeInfo->mDbFile = str;
    }
    if (cf.Lookup(TSTRING(_T("SITEKEYFILE")), str))
    {
        pModeInfo->mSiteKeyFile = str;
    }
    if (cf.Lookup(TSTRING(_T("LOCALKEYFILE")), str))
    {
        pModeInfo->mLocalKeyFile = str;
    }
    if (cf.Lookup(TSTRING(_T("REPORTFILE")), str))
    {
        pModeInfo->mReportFile = str;
    }
    if (cf.Lookup(TSTRING(_T("EDITOR")), str))
    {
        pModeInfo->mEditor = str;
    }
    if (cf.Lookup(TSTRING(_T("LATEPROMPTING")), str))
    {
        if (_tcsicmp(str.c_str(), _T("true")) == 0)
            pModeInfo->mbLatePassphrase = true;
    }
    if (cf.Lookup(TSTRING(_T("RESETACCESSTIME")), str))
    {
        // We do not support reset access time on Unix, so we issue a warning.
        // This used to be a fatal error, however this prevents
        // cross platform config files.
        cTWUtil::PrintErrorMsg(eTWInvalidConfigFileKey(_T("RESETACCESSTIME"), eError::NON_FATAL));
    }
    if (cf.Lookup(TSTRING(_T("LOOSEDIRECTORYCHECKING")), str))
    {
        if (_tcsicmp(str.c_str(), _T("true")) == 0)
            pModeInfo->mfLooseDirs = true;
    }

    util_InitTempDirectory(cf);

    util_InitEmailOptions(pModeInfo, cf);

    // SYSLOG reporting
    if (cf.Lookup(TSTRING(_T("SYSLOGREPORTING")), str))
    {
        if (_tcsicmp(str.c_str(), _T("true")) == 0)
        {
#if SUPPORTS_SYSLOG
            pModeInfo->mbLogToSyslog = true;
#else
            eTWSyslogNotSupported e;
            e.SetFatality(false);
            cErrorReporter::PrintErrorMsg(e);
            pModeInfo->mbLogToSyslog = false;
#endif
        }
        else
            pModeInfo->mbLogToSyslog = false;
    }
    else
    {
        pModeInfo->mbLogToSyslog = false;
    }

    // Crossing file systems
    if (cf.Lookup(TSTRING(_T("CROSSFILESYSTEMS")), str))
    {
        if (_tcsicmp(str.c_str(), _T("true")) == 0)
            pModeInfo->mbCrossFileSystems = true;
        else
            pModeInfo->mbCrossFileSystems = false;
    }

    if (cf.Lookup(TSTRING(_T("HASH_DIRECT_IO")), str))
    {
#if SUPPORTS_DIRECT_IO
        if (_tcsicmp(str.c_str(), _T("true")) == 0)
        {
            pModeInfo->mbDirectIO = true;
            cArchiveSigGen::SetUseDirectIO(true);
        }
#else
        throw eTWDirectIONotSupported();
#endif
    }

    if (cf.Lookup(TSTRING(_T("RESOLVE_IDS_TO_NAMES")), str))
    {
        if (_tcsicmp(str.c_str(), _T("true")) == 0)
            iFSServices::GetInstance()->SetResolveNames(true);
        else
            iFSServices::GetInstance()->SetResolveNames(false);
    }

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

///////////////////////////////////////////////////////////////////////////////
// FillOutCmdLineInfo -- fills out info common to all modes that appears on the
//    command line.
///////////////////////////////////////////////////////////////////////////////
static void FillOutCmdLineInfo(cTWModeCommon* pModeInfo, const cCmdLineParser& cmdLine)
{
    cCmdLineIter iter(cmdLine);
    for (iter.SeekBegin(); !iter.Done(); iter.Next())
    {
        switch (iter.ArgId())
        {
        case cTWCmdLine::VERBOSE:
            pModeInfo->mVerbosity = 2;
            break;
        case cTWCmdLine::SILENT:
            pModeInfo->mVerbosity = 0;
            break;
        case cTWCmdLine::POL_FILE:
            ASSERT(iter.NumParams() > 0); // should be caught by cmd line parser
            pModeInfo->mPolFile = iter.ParamAt(0);
            break;
        case cTWCmdLine::SITE_KEY_FILE:
            ASSERT(iter.NumParams() > 0); // should be caught by cmd line parser
            pModeInfo->mSiteKeyFile = iter.ParamAt(0);
            break;
        case cTWCmdLine::LOCAL_KEY_FILE:
            ASSERT(iter.NumParams() > 0); // should be caught by cmd line parser
            pModeInfo->mLocalKeyFile = iter.ParamAt(0);
            break;
        case cTWCmdLine::DB_FILE:
            ASSERT(iter.NumParams() > 0); // should be caught by cmd line parser
            pModeInfo->mDbFile = iter.ParamAt(0);
            break;
        case cTWCmdLine::REPORT_FILE:
            ASSERT(iter.NumParams() > 0); // should be caught by cmd line parser
            pModeInfo->mReportFile = iter.ParamAt(0);
            break;
        case cTWCmdLine::LOCAL_PASSPHRASE:
        {
            ASSERT(iter.NumParams() > 0); // should be caught by cmd line parser
            pModeInfo->mLocalPassphrase = cStringUtil::TstrToWstr(iter.ParamAt(0));
            pModeInfo->mLocalProvided   = true;
        }
        break;
/*    case cTWCmdLine::NO_BACKUP:
         pModeInfo->mbBackup = false;
         break;
*/    }
    }

    //
    // turn all of the file names into full paths for nice presentation to the user.
    //
    TSTRING fullPath;
    if (iFSServices::GetInstance()->FullPath(fullPath, pModeInfo->mPolFile))
        pModeInfo->mPolFile = fullPath;
    if (iFSServices::GetInstance()->FullPath(fullPath, pModeInfo->mSiteKeyFile))
        pModeInfo->mSiteKeyFile = fullPath;
    if (iFSServices::GetInstance()->FullPath(fullPath, pModeInfo->mLocalKeyFile))
        pModeInfo->mLocalKeyFile = fullPath;
    if (iFSServices::GetInstance()->FullPath(fullPath, pModeInfo->mDbFile))
        pModeInfo->mDbFile = fullPath;
    if (iFSServices::GetInstance()->FullPath(fullPath, pModeInfo->mReportFile))
        pModeInfo->mReportFile = fullPath;


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

    // if a passphrase was passed on the command line, then late prompting should
    // be set to false so that wrong passwords are detected immediately
    if (pModeInfo->mLocalProvided && pModeInfo->mbLatePassphrase)
    {
        // TODO -- I don't know if we should display this or not -- mdb
        //iUserNotify::GetInstance()->Notify(  iUserNotify::V_VERBOSE, TSS_GetString( cTripwire, tripwire::STR_NOTIFY_CHANGE_PROMPT_TIME).c_str() );
        pModeInfo->mbLatePassphrase = false;
    }
}

//#############################################################################
// cTWModeDbInit
//#############################################################################
class cTWModeDbInit_i : public cTWModeCommon
{
public:
    bool mbEncryptDb;

    // ctor can set up some default values
    cTWModeDbInit_i() : cTWModeCommon(), mbEncryptDb(true)
    {
    }
};

///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeDbInit::cTWModeDbInit()
{
    mpData = new cTWModeDbInit_i;
}

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

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeDbInit::InitCmdLineParser(cCmdLineParser& cmdLine)
{
    // initialize the switches common to all modes...
    InitCmdLineCommon(cmdLine);

    cmdLine.AddArg(cTWCmdLine::MODE_INIT, TSTRING(_T("")), TSTRING(_T("init")), cCmdLineParser::PARAM_NONE);
    cmdLine.AddArg(cTWCmdLine::NO_ENCRYPT, TSTRING(_T("e")), TSTRING(_T("no-encryption")), cCmdLineParser::PARAM_NONE);
    cmdLine.AddArg(cTWCmdLine::PARAMS, TSTRING(_T("")), TSTRING(_T("")), cCmdLineParser::PARAM_NONE);

    // mutual exclusion...
    cmdLine.AddMutEx(cTWCmdLine::NO_ENCRYPT, cTWCmdLine::LOCAL_PASSPHRASE);
    cmdLine.AddMutEx(cTWCmdLine::NO_ENCRYPT, cTWCmdLine::LOCAL_KEY_FILE);
}

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeDbInit::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
    // first, fill out everything with the config file info...
    FillOutConfigInfo(mpData, cf);

    // 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 cTWCmdLine::NO_ENCRYPT:
            mpData->mbEncryptDb = false;
            break;
        default:
            // should I do anything, or just ignore this?
            ;
        }
    }

    //----------------------------------------
    // I require the following information:
    //
    // * local key file
    // * site key file
    // * policy file
    // * db file
    //-----------------------------------------
    TEST_INIT_REQUIREMENT((!mpData->mLocalKeyFile.empty()), cTW, tw::STR_ERR_MISSING_LOCAL_KEY);
    TEST_INIT_REQUIREMENT((!mpData->mSiteKeyFile.empty()), cTW, tw::STR_ERR_MISSING_SITE_KEY);
    TEST_INIT_REQUIREMENT((!mpData->mPolFile.empty()), cTW, tw::STR_ERR_MISSING_POLICY);
    TEST_INIT_REQUIREMENT((!mpData->mDbFile.empty()), cTW, tw::STR_ERR_MISSING_DB);


    // make sure that the config file and site key file are in sync...
    //
    if (cTWUtil::VerifyCfgSiteKey(mstrConfigFile, mpData->mSiteKeyFile) == false)
        cTWUtil::PrintErrorMsg(eTWCfgUnencrypted(_T(""), eError::NON_FATAL | eError::SUPRESS_THIRD_MSG));

    // Set the cross file systems flag appropriately.
    cFSDataSourceIter::SetFileSystemCrossing(mpData->mbCrossFileSystems);
    return true;
}

///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWModeDbInit::Execute(cErrorQueue* pQueue)
{
    cFCODatabaseFile           dbFile;
    cFCODatabaseFile::iterator dbIter(dbFile);

    dbFile.SetFileName(mpData->mDbFile);
    try
    {
        // don't go any further if we won't be able to write to the db file...
        cFileUtil::TestFileWritable(mpData->mDbFile);

        // open the keyfile for early passphrase...
        cKeyFile                     keyfile;
        const cElGamalSigPrivateKey* pPrivateKey = 0;
        if (mpData->mbEncryptDb)
        {
            cTWUtil::OpenKeyFile(keyfile, mpData->mLocalKeyFile);
            // open the private key for "early passphrase"
            if (!mpData->mbLatePassphrase)
                pPrivateKey = cTWUtil::CreatePrivateKey(
                    keyfile, mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0, cTWUtil::KEY_LOCAL);
        }

        //Parse the policy file
        cGenreSpecListVector genreSpecList;
        cTWCmdLineUtil::ParsePolicyFile(genreSpecList, mpData->mPolFile, mpData->mSiteKeyFile, pQueue);

#ifdef TW_PROFILE
        cTaskTimer timer("cTripwire::GenerateDatabase");
        timer.Start();
#endif

        iUserNotify::GetInstance()->Notify(1, TSS_GetString(cTripwire, tripwire::STR_GENERATING_DB).c_str());

        uint32 gdbFlags = 0;
        gdbFlags |= (mpData->mbResetAccessTime ? cGenerateDb::FLAG_ERASE_FOOTPRINTS_GD : 0);
        gdbFlags |= (mpData->mbDirectIO ? cGenerateDb::FLAG_DIRECT_IO : 0);

        // loop through the genres
        cGenreSpecListVector::iterator genreIter;
        for (genreIter = genreSpecList.begin(); genreIter != genreSpecList.end(); ++genreIter)
        {
            // make this genre the current genre.
            //
            cGenreSwitcher::GetInstance()->SelectGenre(genreIter->GetGenre());

            // notify the user
            //
            iUserNotify::GetInstance()->Notify(
                iUserNotify::V_NORMAL,
                TSS_GetString(cTripwire, tripwire::STR_PROCESSING_GENRE).c_str(),
                cGenreSwitcher::GetInstance()->GenreToString(cGenreSwitcher::GetInstance()->CurrentGenre(), true));

            // add an entry to the database for each genre we are parsing
            dbFile.AddGenre(genreIter->GetGenre(), &dbIter);

            // copy the spec list to the database...
            dbIter.GetSpecList() = genreIter->GetSpecList();

            // generate the database...
            // TODO -- turn pQueue into an error bucket
            cGenerateDb::Execute(
                dbIter.GetSpecList(), dbIter.GetDb(), dbIter.GetGenreHeader().GetPropDisplayer(), pQueue, gdbFlags);
        }

        cFCODatabaseUtil::CalculateHeader(dbFile.GetHeader(),
                                          mpData->mPolFile,
                                          mstrConfigFile,
                                          mpData->mDbFile,
                                          mstrCmdLine,
                                          cSystemInfo::GetExeStartTime(),
                                          0);

#ifdef TW_PROFILE
        timer.Stop();
#endif

        // write the db to disk...
        if (mpData->mbEncryptDb)
        {
            // open the key file for late passphrase
            if (!pPrivateKey)
            {
                ASSERT(mpData->mbLatePassphrase);
                pPrivateKey = cTWUtil::CreatePrivateKey(
                    keyfile, mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0, cTWUtil::KEY_LOCAL);
            }

            // backup the file we are about to overwrite
            cFileUtil::BackupFile(mpData->mDbFile);

            cTWUtil::WriteDatabase(mpData->mDbFile.c_str(), dbFile, true, pPrivateKey);
            keyfile.ReleasePrivateKey();
        }
        else
        {
            cFileUtil::BackupFile(mpData->mDbFile); // backup the file we are about to overwrite

            cTWUtil::WriteDatabase(mpData->mDbFile.c_str(), dbFile, false, NULL); // false means no encryption
        }
    }
    catch (eError& e)
    {
        if (mpData->mbLogToSyslog)
        {
            TSTRING msg = TSS_GetString(cTripwire, tripwire::STR_SYSLOG_INIT_FAIL_MSG);
            msg.append(cErrorTable::GetInstance()->Get(e.GetID()));

            cSyslog::Log(TSS_GetString(cTripwire, tripwire::STR_SYSLOG_IDENT).c_str(), cSyslog::LOG_ERROR, msg.c_str());
        }

        cTWUtil::PrintErrorMsg(e);
        return 8;
    }

    // everything went ok; return 0
    iUserNotify::GetInstance()->Notify(1, TSS_GetString(cTripwire, tripwire::STR_INIT_SUCCESS).c_str());

    if (mpData->mbLogToSyslog)
    {
        TSTRING msg = TSS_GetString(cTripwire, tripwire::STR_SYSLOG_INIT_MSG) + mpData->mDbFile;
        cSyslog::Log(TSS_GetString(cTripwire, tripwire::STR_SYSLOG_IDENT).c_str(), cSyslog::LOG_SUCCESS, msg.c_str());
    }

    return 0;
}

///////////////////////////////////////////////////////////////////////////////
// GetModeUsage
///////////////////////////////////////////////////////////////////////////////
TSTRING cTWModeDbInit::GetModeUsage(void)
{
    return (TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_INIT));
}

//#############################################################################
// cTWModeIC
//#############################################################################
class cTWModeIC_i : public cTWModeCommon
{
public:
    bool    mbUpdate;         // launch the editor after integrity checking is done
    bool    mbPrintToStdout;  // print a text version of the report to stdout?
    bool    mbEmail;          // email the report to the appropriate parties?
    TSTRING mIgnoreProps;     // symbols of properties to ignore
    bool    mbEncryptReport;  // should the report be encrypted?
    int     mSeverityLevel;   // only policy rules greater than or equal to the specified level will be checked.
    bool    mbTrimBySeverity; // do we use mSeverityLevel to trim the spec list?
    TSTRING mSeverityName;    // gets mapped to number, then treated like mSeverityLevel
    TSTRING mRuleName;        // only the named rule will be checked
    TSTRING mGenreName;       // if not empty, specifies the genre to check
    bool    mbSecureMode;     // are we in extra-pedantic mode? (only valid with mbUpdate == true)

    //TSTRING     mCmdLine;               // entire command line
    std::vector<TSTRING> mFilesToCheck;

    // ctor can set up some default values
    cTWModeIC_i()
        : cTWModeCommon(),
          mbUpdate(false),
          mbPrintToStdout(true),
          mbEmail(false),
          mbEncryptReport(false),
          mSeverityLevel(-1),
          mbTrimBySeverity(false),
          mbSecureMode(false)
    {
    }
};

///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeIC::cTWModeIC()
{
    mpData = new cTWModeIC_i;
}

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

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeIC::InitCmdLineParser(cCmdLineParser& cmdLine)
{
    // initialize the switches common to all modes...
    InitCmdLineCommon(cmdLine);

    cmdLine.AddArg(cTWCmdLine::MODE_CHECK, TSTRING(_T("")), TSTRING(_T("check")), cCmdLineParser::PARAM_NONE);
    cmdLine.AddArg(cTWCmdLine::INTER_UPDATE, TSTRING(_T("I")), TSTRING(_T("interactive")), cCmdLineParser::PARAM_NONE);
    cmdLine.AddArg(cTWCmdLine::EDITOR, TSTRING(_T("V")), TSTRING(_T("visual")), cCmdLineParser::PARAM_ONE);
    cmdLine.AddArg(cTWCmdLine::NO_TTY, TSTRING(_T("n")), TSTRING(_T("no-tty-output")), cCmdLineParser::PARAM_NONE);
    cmdLine.AddArg(cTWCmdLine::MAIL_REPORT, TSTRING(_T("M")), TSTRING(_T("email-report")), cCmdLineParser::PARAM_NONE);
    cmdLine.AddArg(cTWCmdLine::IGNORE_PROP, TSTRING(_T("i")), TSTRING(_T("ignore")), cCmdLineParser::PARAM_ONE);
    cmdLine.AddArg(
        cTWCmdLine::ENCRYPT_REPORT, TSTRING(_T("E")), TSTRING(_T("signed-report")), cCmdLineParser::PARAM_NONE);
    cmdLine.AddArg(cTWCmdLine::SEVERITY_LEVEL, TSTRING(_T("l")), TSTRING(_T("severity")), cCmdLineParser::PARAM_ONE);
    //cmdLine.AddArg(cTWCmdLine::SEVERITY_NAME,  TSTRING(_T("V")), TSTRING(_T("severityname")),  cCmdLineParser::PARAM_ONE);
    cmdLine.AddArg(cTWCmdLine::RULE_NAME, TSTRING(_T("R")), TSTRING(_T("rule-name")), cCmdLineParser::PARAM_ONE);
    cmdLine.AddArg(cTWCmdLine::GENRE_NAME, TSTRING(_T("x")), TSTRING(_T("section")), cCmdLineParser::PARAM_ONE);
    cmdLine.AddArg(cTWCmdLine::PARAMS, TSTRING(_T("")), TSTRING(_T("")), cCmdLineParser::PARAM_MANY);
    cmdLine.AddArg(cTWCmdLine::HEXADECIMAL, TSTRING(_T("h")), TSTRING(_T("hexadecimal")), cCmdLineParser::PARAM_NONE);

    // multiple levels of reporting
    cmdLine.AddArg(
        cTWCmdLine::REPORTLEVEL, TSTRING(_T("t")), TSTRING(_T("email-report-level")), cCmdLineParser::PARAM_ONE);

    // mutual exclusion...
    // you can't specify any of these 3 things together...
    cmdLine.AddMutEx(cTWCmdLine::SEVERITY_LEVEL, cTWCmdLine::RULE_NAME);
    cmdLine.AddDependency(cTWCmdLine::REPORTLEVEL, cTWCmdLine::MAIL_REPORT);
    // Report error if the user has specified an editor and we're not in interactive update
    // mode:  This needs to happen here, so we don't nail someone for having the editor
    // specified in the config. file.  We should only be this picky if both parameters are
    // passed on the command line.
    cmdLine.AddDependency(cTWCmdLine::EDITOR, cTWCmdLine::INTER_UPDATE);
}

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeIC::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
    // We will use this to access the parsed cmdLine.
    cCmdLineIter iter(cmdLine);

    // First, fill out everything with the config file info...
    FillOutConfigInfo(mpData, cf);

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

    // now do the stuff specific to this mode..

    for (iter.SeekBegin(); !iter.Done(); iter.Next())
    {
        switch (iter.ArgId())
        {
        case cTWCmdLine::INTER_UPDATE:
            mpData->mbUpdate = true;
            break;
        case cTWCmdLine::EDITOR:
            ASSERT(iter.NumParams() > 0);
            mpData->mEditor = iter.ParamAt(0);
            break;
        case cTWCmdLine::NO_TTY:
            mpData->mbPrintToStdout = false;
            break;
        case cTWCmdLine::MAIL_REPORT:
            mpData->mbEmail = true;
            break;
        case cTWCmdLine::IGNORE_PROP:
            ASSERT(iter.NumParams() > 0);
            mpData->mIgnoreProps = iter.ParamAt(0);
            break;
        case cTWCmdLine::ENCRYPT_REPORT:
            mpData->mbEncryptReport = true;
            break;
        case cTWCmdLine::SEVERITY_LEVEL:
            ASSERT(iter.NumParams() > 0);
            if (_istdigit(iter.ParamAt(0).c_str()[0]))
            {
                // interpret severity value
                int             iSev;
                cSeverityLimits sl;
                if (!sl.InterpretInt(iter.ParamAt(0), &iSev))
                    throw eICBadSevVal(iter.ParamAt(0));

                mpData->mSeverityLevel   = iSev;
                mpData->mbTrimBySeverity = true;
            }
            else
            {
                mpData->mSeverityName = iter.ParamAt(0);
            }
            break;
        case cTWCmdLine::REPORTLEVEL:
        {
            if (iter.ParamAt(0) == _T("0"))
                mpData->mEmailReportLevel = cTextReportViewer::SINGLE_LINE;
            else if (iter.ParamAt(0) == _T("1"))
                mpData->mEmailReportLevel = cTextReportViewer::PARSEABLE;
            else if (iter.ParamAt(0) == _T("2"))
                mpData->mEmailReportLevel = cTextReportViewer::SUMMARY_ONLY;
            else if (iter.ParamAt(0) == _T("3"))
                mpData->mEmailReportLevel = cTextReportViewer::CONCISE_REPORT;
            else if (iter.ParamAt(0) == _T("4"))
                mpData->mEmailReportLevel = cTextReportViewer::FULL_REPORT;
            else
            {
                // They specified an illegal level, error.
                TSTRING errStr = _T("Invalid Level: ");
                errStr += iter.ParamAt(0);
                throw eTWInvalidReportLevel(errStr);
            }
        }
        //done with report-level stuff.
        break;
        case cTWCmdLine::RULE_NAME:
            ASSERT(iter.NumParams() > 0);
            mpData->mRuleName = iter.ParamAt(0);
            break;
        case cTWCmdLine::GENRE_NAME:
            ASSERT(iter.NumParams() > 0);
            mpData->mGenreName = iter.ParamAt(0);
            break;
        case cTWCmdLine::HEXADECIMAL:
            cArchiveSigGen::SetHex(true);
            break;

        case cTWCmdLine::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:
            // should I do anything, or just ignore this?
            ;
        }
    }


    // if an editor is not provided in cfg or on cmdline, try to get it from environment
    if (mpData->mbUpdate && mpData->mEditor.empty())
        util_GetEditor(mpData->mEditor);

    //----------------------------------------
    // I require the following information:
    //
    // * local key file
    // * site key file
    // * policy file
    // * db file
    // * report file
    // * editor (if interactive is specified)
    //-----------------------------------------
    TEST_INIT_REQUIREMENT((!mpData->mLocalKeyFile.empty()), cTW, tw::STR_ERR_MISSING_LOCAL_KEY);
    TEST_INIT_REQUIREMENT((!mpData->mSiteKeyFile.empty()), cTW, tw::STR_ERR_MISSING_SITE_KEY);
    TEST_INIT_REQUIREMENT((!mpData->mPolFile.empty()), cTW, tw::STR_ERR_MISSING_POLICY);
    TEST_INIT_REQUIREMENT((!mpData->mDbFile.empty()), cTW, tw::STR_ERR_MISSING_DB);
    TEST_INIT_REQUIREMENT((!mpData->mReportFile.empty()), cTW, tw::STR_ERR_MISSING_REPORT);
    TEST_INIT_REQUIREMENT((!(mpData->mEditor.empty() && mpData->mbUpdate)), cTW, tw::STR_ERR_MISSING_EDITOR);

    ///////////////////////////////////////////
    // do some email-related verifications
    ///////////////////////////////////////////
    if (mpData->mbEmail)
    {
        // make sure that email reporting and a file list were both not specified...
        TEST_INIT_REQUIREMENT(mpData->mFilesToCheck.empty(), cTripwire, tripwire::STR_ERR_IC_EMAIL_AND_FILES);

        // make sure that we have a valid mail method
        TEST_INIT_REQUIREMENT(
            (cMailMessage::NO_METHOD != mpData->mMailMethod), cTripwire, tripwire::STR_ERR_NO_MAIL_METHOD);
        TEST_INIT_REQUIREMENT(
            (cMailMessage::INVALID_METHOD != mpData->mMailMethod), cTripwire, tripwire::STR_ERR_INVALID_MAIL_METHOD);

        // error if method is SENDMAIL and no MAILPROGRAM
        TEST_INIT_REQUIREMENT((cMailMessage::MAIL_BY_PIPE != mpData->mMailMethod || !mpData->mMailProgram.empty()),
                              cTripwire,
                              tripwire::STR_ERR_MISSING_MAILPROGRAM);
    }

    // make sure that the config file and site key file are in sync...
    //
    if (cTWUtil::VerifyCfgSiteKey(mstrConfigFile, mpData->mSiteKeyFile) == false)
        cTWUtil::PrintErrorMsg(eTWCfgUnencrypted(_T(""), eError::NON_FATAL | eError::SUPRESS_THIRD_MSG));

    // Set the cross file systems flag appropriately.
    cFSDataSourceIter::SetFileSystemCrossing(mpData->mbCrossFileSystems);
    return true;
}


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

    cFCODatabaseFile           dbFile;
    cFCODatabaseFile::iterator dbIter(dbFile);
    cFCOReport                 report;
    cFCOReportHeader           reportHeader;
    bool                       bEncrypted, bCheckMade = false, bEmailError = false;
    try
    {

        // don't go any further if we won't be able to write the report to disk...
        cFileUtil::TestFileWritable(mpData->mReportFile);

        // open the database; note that ReadDatabase will set bEncrypted and open the
        // key file if necessary
        cKeyFile localKeyfile;
        cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);

        cTWUtil::ReadDatabase(mpData->mDbFile.c_str(), dbFile, localKeyfile.GetPublicKey(), bEncrypted);

        //
        // give a warning if the user that created the database is not the same as the user running right now...
        //
        TSTRING userName;
        if (iFSServices::GetInstance()->GetCurrentUserName(userName))
        {
            if (dbFile.GetHeader().GetCreator().compare(userName.c_str()) != 0)
            {
                // TODO -- move these strings to the string table
                TOSTRINGSTREAM str;
                str << TSS_GetString(cTripwire, tripwire::STR_ERR2_DIFFERENT_USERS1) << dbFile.GetHeader().GetCreator()
                    << TSS_GetString(cTripwire, tripwire::STR_ERR2_DIFFERENT_USERS2) << userName << std::ends;
                cTWUtil::PrintErrorMsg(eICDifferentUsers(str.str(), eError::NON_FATAL));
            }
        }

        // give a stern warning if the database is not encrypted
        if (!bEncrypted && iUserNotify::GetInstance()->GetVerboseLevel() > iUserNotify::V_SILENT)
        {
            cTWUtil::PrintErrorMsg(eTWDbNotEncrypted(_T(""), eError::NON_FATAL | eError::SUPRESS_THIRD_MSG));
        }

        if ((!bEncrypted) && (mpData->mLocalProvided) && (!mpData->mbEncryptReport))
        {
            //Issue a warning if a passphrase has been supplied, and the database is NOT encrypted.
            cTWUtil::PrintErrorMsg(eTWPassForUnencryptedDb(_T(""), eError::NON_FATAL));
        }


        // get the password here if they are saving an encrypted report
        const cElGamalSigPrivateKey* pPrivateKey = 0;
        if (mpData->mbEncryptReport)
        {
            // if the key file isn't open (ie -- the report wasn't encrypted) then we need to
            // open it now...
            cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);

            // open the private key for "early passphrase"
            if (!mpData->mbLatePassphrase)
            {
                // OK, here is the deal: if the user specified a password on the command line
                // and we are going to do an interactive update after the IC and both the report
                // and db are encrypted, then we need to save the passphrase so it doesn't get
                // scrambled by CreatePrivateKey
                if (mpData->mLocalProvided && mpData->mbUpdate && bEncrypted)
                {
                    wc16_string passPhrase = mpData->mLocalPassphrase.c_str();
                    pPrivateKey = cTWUtil::CreatePrivateKey(localKeyfile, passPhrase.c_str(), cTWUtil::KEY_LOCAL);
                }
                else
                    pPrivateKey =
                        cTWUtil::CreatePrivateKey(localKeyfile,
                                                  mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0,
                                                  cTWUtil::KEY_LOCAL);
            }
        }

        // either integrity check using the policy file or a list of files
        if (mpData->mFilesToCheck.size() != 0)
        {
            // we are checking files...
            iUserNotify::GetInstance()->Notify(1, TSS_GetString(cTripwire, tripwire::STR_IC_FILES).c_str());
            ////
            // tell the user we are ignoring severity level and rule name, if they specified them...
            //
            if (!mpData->mRuleName.empty())
                iUserNotify::GetInstance()->Notify(
                    iUserNotify::V_NORMAL, TSS_GetString(cTripwire, tripwire::STR_IC_IGNORING_RULE_NAME).c_str());
            if (mpData->mbTrimBySeverity)
                iUserNotify::GetInstance()->Notify(iUserNotify::V_NORMAL,
                                                   TSS_GetString(cTripwire, tripwire::STR_IC_IGNORING_SEV_NUM).c_str());
            if (!mpData->mSeverityName.empty())
                iUserNotify::GetInstance()->Notify(
                    iUserNotify::V_NORMAL, TSS_GetString(cTripwire, tripwire::STR_IC_IGNORING_SEV_NAME).c_str());
            if (!mpData->mGenreName.empty())
                iUserNotify::GetInstance()->Notify(
                    iUserNotify::V_NORMAL, TSS_GetString(cTripwire, tripwire::STR_IC_IGNORING_GENRE_NAME).c_str());
            //
            ////

            //**********************************
            // new, experimental stuff.. trying out new command line interface
            //
            cTWUtil::GenreObjList listOut;
            cTWUtil::ParseObjectList(listOut, mpData->mFilesToCheck);
            //
            // now, we will iterate over the list, performing an integrity check for each genre
            //
            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());
                    TW_UNIQUE_PTR<iParserGenreUtil> pParseUtil(iTWFactory::GetInstance()->CreateParserGenreUtil());
                    //
                    // I have to turn this into a list of cFCONames
                    //
                    std::list<cFCOName> fcoNames;
                    for (cTWUtil::ObjList::iterator it = genreIter->second.begin(); it != genreIter->second.end(); ++it)
                    {
                        // if this is not an absolute path, warn and continue...
                        //
                        try
                        {
                            fcoNames.push_back(cTWUtil::ParseObjectName(*it));
                        }
                        catch (eError& e)
                        {
                            e.SetFatality(false);
                            pQueue->AddError(e);
                            report.GetErrorQueue()->AddError(e);
                        }
                    }
                    // TODO -- emit "processing XXX"

                    cIntegrityCheck ic(
                        (cGenre::Genre)genreIter->first, dbIter.GetSpecList(), dbIter.GetDb(), report, pQueue);

                    //If any sort of exception escapes the IC, make sure it goes in the report.
                    try
                    {
                        uint32 icFlags = 0;
                        icFlags |= (mpData->mfLooseDirs ? cIntegrityCheck::FLAG_LOOSE_DIR : 0);
                        icFlags |= (mpData->mbResetAccessTime ? cIntegrityCheck::FLAG_ERASE_FOOTPRINTS_IC : 0);
                        icFlags |= (mpData->mbDirectIO ? cIntegrityCheck::FLAG_DIRECT_IO : 0);

                        ic.ExecuteOnObjectList(fcoNames, icFlags);
                    }
                    catch (eError& e)
                    {
                        if (pQueue)
                            pQueue->AddError(e);
                    }
                    catch (std::exception& e)
                    {
                        if (pQueue)
                            pQueue->AddError(eIC(e.what()));
                    }
                    catch (...)
                    {
                        if (pQueue)
                            pQueue->AddError(eIC("Unknown"));
                    }
                    // put all info into report
                    cFCOReportGenreIter rgi(report);
                    rgi.SeekToGenre((cGenre::Genre)genreIter->first);
                    rgi.GetGenreHeader().SetPropDisplayer(iTWFactory::GetInstance()->CreatePropDisplayer());
                    rgi.GetGenreHeader().GetPropDisplayer()->Merge(dbIter.GetGenreHeader().GetPropDisplayer());
                    rgi.GetGenreHeader().SetObjectsScanned(ic.ObjectsScanned());
                }
                else
                {
                    //TODO -- what is the right thing to do here?
                    //    error and continue or exit?
                    //
                    eTWDbDoesntHaveGenre e(
                        cGenreSwitcher::GetInstance()->GenreToString((cGenre::Genre)genreIter->first, true));
                    e.SetFatality(false);
                    cTWUtil::PrintErrorMsg(e);
                }
            }
            //
            //**********************************
        }
        else
        {
            // we are checking a subset of the policy file

            // parse the policy file
            cGenreSpecListVector genreSpecList;
            cTWCmdLineUtil::ParsePolicyFile(genreSpecList, mpData->mPolFile, mpData->mSiteKeyFile, pQueue);

            //
            // make sure the parsed policy is equivelant to the one in the database...
            // TODO -- should this go in the report? should I make a report or just bail? I guess
            //    that for now, I will bail. (it throws an exception on failure
            //
            cTWCmdLineUtil::VerifyPolicy(genreSpecList, dbFile);

            //
            // if they are only checking one genre, figure out which one it is ...
            //
            cGenre::Genre genreToCheck = cGenre::GENRE_INVALID;
            if (!mpData->mGenreName.empty())
            {
                genreToCheck = cGenreSwitcher::GetInstance()->StringToGenre(mpData->mGenreName.c_str());
                if (genreToCheck == cGenre::GENRE_INVALID)
                {
                    throw eICUnknownGenre(mpData->mGenreName);
                }
                if (!cGenreSwitcher::GetInstance()->IsGenreAppropriate(genreToCheck))
                {
                    throw eICInappropriateGenre(mpData->mGenreName);
                }

                iUserNotify::GetInstance()->Notify(iUserNotify::V_NORMAL,
                                                   TSS_GetString(cTripwire, tripwire::STR_CHECKING_GENRE).c_str(),
                                                   cGenreSwitcher::GetInstance()->GenreToString(genreToCheck));
            }


            // iterate over the genres...
            //
            cGenreSpecListVector::iterator genreIter;
            for (genreIter = genreSpecList.begin(); genreIter != genreSpecList.end(); ++genreIter)
            {
                // if they are only checking one genre and it is not this one, then
                // skip everything else...
                //
                if ((genreToCheck != cGenre::GENRE_INVALID) && (genreToCheck != genreIter->GetGenre()))
                    continue;

                // make this genre the current genre.
                //
                cGenreSwitcher::GetInstance()->SelectGenre(genreIter->GetGenre());
                //
                // seek the database iter to this genre...
                //
                dbIter.SeekToGenre(genreIter->GetGenre());
                if (dbIter.Done())
                {
                    // TODO -- what is the right thing to do here? throw or warn and continue?
                    //    I think that I should add it to the report's error queue and spit it to
                    //    stderr and continue
                    //
                    TCERR << _T("Genre ") << cGenreSwitcher::GetInstance()->GenreToString(genreIter->GetGenre())
                          << _T(" not in database; skipping.") << std::endl;
                    continue;
                }
                // message to the user...
                //
                iUserNotify::GetInstance()->Notify(
                    iUserNotify::V_NORMAL,
                    TSS_GetString(cTripwire, tripwire::STR_PROCESSING_GENRE).c_str(),
                    cGenreSwitcher::GetInstance()->GenreToString(cGenreSwitcher::GetInstance()->CurrentGenre(), true));

                cFCOSpecList specList = genreIter->GetSpecList();

                // trim ignored properties from the specs...
                if (!mpData->mIgnoreProps.empty())
                    cTWCmdLineUtil::TrimPropsFromSpecs(specList, mpData->mIgnoreProps);

                // trim ignored rule names from the specs...
                if (!mpData->mRuleName.empty())
                    cTWCmdLineUtil::TrimSpecsByName(specList, mpData->mRuleName);

                // trim spec list based on severity
                if (mpData->mbTrimBySeverity)
                    cTWCmdLineUtil::TrimSpecsBySeverity(specList, mpData->mSeverityLevel);

                // trim specs by severity name
                if (!mpData->mSeverityName.empty())
                    cTWCmdLineUtil::TrimSpecsBySeverityName(specList, mpData->mSeverityName);

                //
                // only do the integrity check if there is something to check!
                //
                if (!specList.IsEmpty())
                {
                    bCheckMade = true; //we've checked at least one rule.

                    // do the integrity check...
                    iUserNotify::GetInstance()->Notify(1,
                                                       TSS_GetString(cTripwire, tripwire::STR_INTEGRITY_CHECK).c_str());

#ifdef TW_PROFILE
                    cTaskTimer timer("cTripwire::IntegrityCheck");
                    timer.Start();
#endif
                    cIntegrityCheck ic((cGenre::Genre)dbIter.GetGenre(), specList, dbIter.GetDb(), report, pQueue);

                    //If any sort of exception escapes the IC, make sure it goes in the report.
                    try
                    {
                        uint32 icFlags = 0;
                        icFlags |= (mpData->mfLooseDirs ? cIntegrityCheck::FLAG_LOOSE_DIR : 0);
                        icFlags |= (mpData->mbResetAccessTime ? cIntegrityCheck::FLAG_ERASE_FOOTPRINTS_IC : 0);
                        icFlags |= (mpData->mbDirectIO ? cIntegrityCheck::FLAG_DIRECT_IO : 0);

                        ic.Execute(icFlags);
                    }
                    catch (eError& e)
                    {
                        if (pQueue)
                            pQueue->AddError(e);
                    }
                    catch (std::exception& e)
                    {
                        if (pQueue)
                            pQueue->AddError(eIC(e.what()));
                    }
                    catch (...)
                    {
                        if (pQueue)
                            pQueue->AddError(eIC("Unknown"));
                    }

                    // put all display info into report
                    cFCOReportGenreIter rgi(report);
                    rgi.SeekToGenre((cGenre::Genre)dbIter.GetGenre());
                    rgi.GetGenreHeader().SetPropDisplayer(iTWFactory::GetInstance()->CreatePropDisplayer());
                    rgi.GetGenreHeader().GetPropDisplayer()->Merge(dbIter.GetGenreHeader().GetPropDisplayer());
                    rgi.GetGenreHeader().SetObjectsScanned(ic.ObjectsScanned());

#ifdef TW_PROFILE
                    timer.Stop();
#endif
                }
                else
                {
                    iUserNotify::GetInstance()->Notify(
                        iUserNotify::V_NORMAL,
                        TSS_GetString(cTripwire, tripwire::STR_ERR_IC_NO_SPECS_LEFT).c_str(),
                        cGenreSwitcher::GetInstance()->GenreToString(genreIter->GetGenre(), true));
                }
            }
        }

        // save out the report...
        cFCOReportUtil::CalculateHeaderInfo(reportHeader,
                                            mpData->mPolFile,
                                            mstrConfigFile,
                                            mpData->mDbFile,
                                            mstrCmdLine,
                                            cSystemInfo::GetExeStartTime(),
                                            dbFile.GetHeader().GetLastDBUpdateTime());

        cFCOReportUtil::FinalizeReport(report);

        // email the report if that is desired...
        if (mpData->mbEmail)
        {
            bEmailError = cTWCmdLineUtil::EmailReport(reportHeader, report, mpData) == 0;

            // We should notify them that no email was sent should the following condition
            // apply.
            if (!bCheckMade)
            {
                iUserNotify::GetInstance()->Notify(
                    iUserNotify::V_NORMAL, TSS_GetString(cTripwire, tripwire::STR_IC_NOEMAIL_SENT).c_str(), _T(""));
            }
        }

        // write the report to disk...
        if (mpData->mbEncryptReport)
        {
            // get the private key for "late passphrase"
            if (!pPrivateKey)
            {
                ASSERT(mpData->mbLatePassphrase);
                // OK, here is the deal: if the user specified a password on the command line
                // and we are going to do an interactive update after the IC and both the report
                // and db are encrypted, then we need to save the passphrase so it doesn't get
                // scrambled by CreatePrivateKey
                if (mpData->mLocalProvided && mpData->mbUpdate && bEncrypted)
                {
                    wc16_string passPhrase = mpData->mLocalPassphrase.c_str();
                    pPrivateKey = cTWUtil::CreatePrivateKey(localKeyfile, passPhrase.c_str(), cTWUtil::KEY_LOCAL);
                }
                else
                    pPrivateKey =
                        cTWUtil::CreatePrivateKey(localKeyfile,
                                                  mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0,
                                                  cTWUtil::KEY_LOCAL);
            }

            // backup the file we are about to overwrite
            cFileUtil::BackupFile(mpData->mReportFile);

            // write the report
            cTWUtil::WriteReport(mpData->mReportFile.c_str(), reportHeader, report, true, pPrivateKey);
            localKeyfile.ReleasePrivateKey();
        }
        else
        {
            cFileUtil::BackupFile(mpData->mReportFile); // backup the file we are about to overwrite
            cTWUtil::WriteReport(
                mpData->mReportFile.c_str(), reportHeader, report, false, NULL); // false means no encryption
        }

        // print a text version of the report to stdout...
        if (mpData->mbPrintToStdout)
        {
            // print a couple of new lines so that the report isn't butted up against the rest of the output.
            iUserNotify::GetInstance()->Notify(iUserNotify::V_NORMAL, _T("\n\n"));
            cTextReportViewer trv(reportHeader, report);
            trv.PrintTextReport(_T("-"), cTextReportViewer::SUMMARY_ONLY);
        }
    }
    catch (eError& e)
    {
        if (mpData->mbLogToSyslog)
        {
            TSTRING msg = TSS_GetString(cTripwire, tripwire::STR_SYSLOG_IC_FAIL_MSG);
            msg.append(cErrorTable::GetInstance()->Get(e.GetID()));

            cSyslog::Log(TSS_GetString(cTripwire, tripwire::STR_SYSLOG_IDENT).c_str(), cSyslog::LOG_ERROR, msg.c_str());
        }

        cTWUtil::PrintErrorMsg(e);
        return 8;
        //TODO -- send a sad email here if mbEmail == true
    }

    // tell the user the report was saved
    iUserNotify::GetInstance()->Notify(1, TSS_GetString(cTripwire, tripwire::STR_IC_SUCCESS).c_str());

    if (mpData->mbLogToSyslog)
    {
        cTextReportViewer trv(reportHeader, report);
        TSTRING           msg = TSS_GetString(cTripwire, tripwire::STR_SYSLOG_IC_MSG);
        msg.append(mpData->mDbFile);
        msg.append(_T(" "));
        msg.append(trv.SingleLineReport());
        cSyslog::Log(TSS_GetString(cTripwire, tripwire::STR_SYSLOG_IDENT).c_str(), cSyslog::LOG_SUCCESS, msg.c_str());
    }

    // go directly into interactive update if that is what they want...
    //
    if (mpData->mbUpdate)
    {
        cTWModeDbUpdate dbUpdate;
        dbUpdate.Init(mpData, &dbFile, &reportHeader, &report, bEncrypted);
        int ret = dbUpdate.Execute(pQueue);

        if (ret != 0)
            return ret;
    }

    // the return level is based on the type of violations that were found:
    // 1 = added   files
    // 2 = removed files
    // 4 = changed files
    // 8 = ERROR_IC = Execution Error
    if (bEmailError)
        return (ERROR_IC | cTWCmdLineUtil::GetICReturnValue(report));
    else
        return cTWCmdLineUtil::GetICReturnValue(report);
}

///////////////////////////////////////////////////////////////////////////////
// GetModeUsage
///////////////////////////////////////////////////////////////////////////////
TSTRING cTWModeIC::GetModeUsage(void)
{
    return (TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_CHECK));
}

//#############################################################################
// cTWModeDbUpdate
//#############################################################################
class cTWModeDbUpdate_i : public cTWModeCommon
{
public:
    bool mbInteractive; // don't do interactive update; just integrate the report file
    bool mbSecureMode;  // are we in extra-pedantic mode?
    //std::string  mSitePassphrase;  // pass phrase for site key
    //bool    mSiteProvided;

    // these members are only used when this mode is entered after integrity checking.
    // they are set to variables that are on cTWModeIC's stack, so they should _not_ be deleted.
    // if they are non-null when Execute() is called, they should be used as the db and report.
    bool              mbEncryptDb;
    cFCOReport*       mpReport;
    cFCODatabaseFile* mpDbFile;
    cFCOReportHeader* mpReportHeader;

    // ctor can set up some default values
    cTWModeDbUpdate_i()
        : cTWModeCommon(),
          mbInteractive(true),
          mbSecureMode(true),
          mbEncryptDb(true),
          /*mSiteProvided(false),*/ mpReport(0),
          mpDbFile(0),
          mpReportHeader(0)
    {
    }
};

///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeDbUpdate::cTWModeDbUpdate()
{
    mpData = new cTWModeDbUpdate_i;
}


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

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeDbUpdate::InitCmdLineParser(cCmdLineParser& cmdLine)
{
    // initialize the switches common to all modes...
    InitCmdLineCommon(cmdLine);

    cmdLine.AddArg(cTWCmdLine::MODE_UPDATE_DB, TSTRING(_T("")), TSTRING(_T("update")), cCmdLineParser::PARAM_NONE);
    cmdLine.AddArg(cTWCmdLine::ACCEPT_ALL, TSTRING(_T("a")), TSTRING(_T("accept-all")), cCmdLineParser::PARAM_NONE);
    cmdLine.AddArg(cTWCmdLine::SECURE_MODE, TSTRING(_T("Z")), TSTRING(_T("secure-mode")), cCmdLineParser::PARAM_ONE);
    cmdLine.AddArg(cTWCmdLine::EDITOR, TSTRING(_T("V")), TSTRING(_T("visual")), cCmdLineParser::PARAM_ONE);
    cmdLine.AddArg(cTWCmdLine::PARAMS, TSTRING(_T("")), TSTRING(_T("")), cCmdLineParser::PARAM_NONE);

    // mutual exclusion...
    cmdLine.AddMutEx(cTWCmdLine::ACCEPT_ALL, cTWCmdLine::EDITOR);
}

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeDbUpdate::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
    // first, fill out everything with the config file info...
    FillOutConfigInfo(mpData, cf);

    // 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 cTWCmdLine::ACCEPT_ALL:
            mpData->mbInteractive = false;
            break;
        case cTWCmdLine::SECURE_MODE:
            ASSERT(iter.NumParams() > 0);
            if (iter.ParamAt(0).compare(_T("high")) == 0)
                mpData->mbSecureMode = true;
            else if (iter.ParamAt(0).compare(_T("low")) == 0)
                mpData->mbSecureMode = false;
            else
            {
                // invalid parameter to secure mode switch...
                // TODO -- print this to stderr; how do I display (1) the switch name
                // and (2) the possible values?
                // TODO -- move {high, low} somewhere else
                TCERR << TSS_GetString(cTripwire, tripwire::STR_ERR_BAD_PARAM) << _T(" : ") << iter.ParamAt(0).c_str()
                      << _T("; must be {high, low}") << std::endl;
                return false;
            }
            break;
        case cTWCmdLine::EDITOR:
            ASSERT(iter.NumParams() > 0);
            mpData->mEditor = iter.ParamAt(0);
            break;
        default:
            // should I do anything, or just ignore this?
            ;
        }
    }

    // if an editor is not provided in cfg or on cmdline, try to get it from environment
    if (mpData->mbInteractive && mpData->mEditor.empty())
        util_GetEditor(mpData->mEditor);

    //----------------------------------------
    // I require the following information:
    //
    // * local key file
    // * site key file
    // * policy file
    // * db file
    // * report file
    // * editor (if accept all is not specified)
    //-----------------------------------------
    TEST_INIT_REQUIREMENT((!mpData->mLocalKeyFile.empty()), cTW, tw::STR_ERR_MISSING_LOCAL_KEY);
    TEST_INIT_REQUIREMENT((!mpData->mSiteKeyFile.empty()), cTW, tw::STR_ERR_MISSING_SITE_KEY);
    TEST_INIT_REQUIREMENT((!mpData->mPolFile.empty()), cTW, tw::STR_ERR_MISSING_POLICY);
    TEST_INIT_REQUIREMENT((!mpData->mDbFile.empty()), cTW, tw::STR_ERR_MISSING_DB);
    TEST_INIT_REQUIREMENT((!mpData->mReportFile.empty()), cTW, tw::STR_ERR_MISSING_REPORT);
    TEST_INIT_REQUIREMENT(((!mpData->mEditor.empty()) || (!mpData->mbInteractive)), cTW, tw::STR_ERR_MISSING_EDITOR);

    // make sure that the config file and site key file are in sync...
    //
    if (cTWUtil::VerifyCfgSiteKey(mstrConfigFile, mpData->mSiteKeyFile) == false)
        cTWUtil::PrintErrorMsg(eTWCfgUnencrypted(_T(""), eError::NON_FATAL | eError::SUPRESS_THIRD_MSG));

    // Set the cross file systems flag appropriately.
    cFSDataSourceIter::SetFileSystemCrossing(mpData->mbCrossFileSystems);
    return true;
}

void cTWModeDbUpdate::Init(
    const cTWModeIC_i* pICData, cFCODatabaseFile* dbFile, cFCOReportHeader* prh, cFCOReport* pReport, bool bEncryptDb)
{
    mpData->mbInteractive = true; // always interactive
    mpData->mbSecureMode  = pICData->mbSecureMode;
    //mpData->mbBackup      = pICData->mbBackup;
    mpData->mDbFile          = pICData->mDbFile;
    mpData->mLocalKeyFile    = pICData->mLocalKeyFile;
    mpData->mLocalPassphrase = pICData->mLocalPassphrase;
    mpData->mLocalProvided   = pICData->mLocalProvided;
    mpData->mPolFile         = pICData->mPolFile;
    mpData->mReportFile      = pICData->mReportFile;
    mpData->mSiteKeyFile     = pICData->mSiteKeyFile;
    mpData->mVerbosity       = pICData->mVerbosity;
    mpData->mEditor          = pICData->mEditor;
    mpData->mbLatePassphrase = pICData->mbLatePassphrase;


    mpData->mbEncryptDb = bEncryptDb;
    mpData->mpDbFile    = dbFile;

    if (pReport)
    {
        ASSERT(prh != 0);
        mpData->mpReport       = pReport;
        mpData->mpReportHeader = prh;
    }
    else
    {
        mpData->mpReport       = 0;
        mpData->mpReportHeader = 0;
    }
}


///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWModeDbUpdate::Execute(cErrorQueue* pQueue)
{
    try
    {
        // don't go any further if we won't be able to write to the db file...
        cFileUtil::TestFileWritable(mpData->mDbFile);

        // open the database
        cFCODatabaseFile dbFile;
        cKeyFile         localKeyfile;
        bool             bDbEncrypted, bReportEncrypted;

        cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);

        if (!mpData->mpDbFile)
        {
            // open the database; note that ReadDatabase will set bEncrypted and open the
            // key file if necessary
            cTWUtil::ReadDatabase(mpData->mDbFile.c_str(), dbFile, localKeyfile.GetPublicKey(), bDbEncrypted);
            mpData->mpDbFile = &dbFile;
        }
        else
        {
            // we were passed the database; we should open the key file now if the db needs
            // to be saved encrypted...
            if (mpData->mbEncryptDb)
                bDbEncrypted = true;
            else
                bDbEncrypted = false;
        }

        if ((!bDbEncrypted) && (mpData->mLocalProvided))
        {
            //Issue a warning if a passphrase has been supplied, and the database is NOT encrypted.
            cTWUtil::PrintErrorMsg(eTWPassForUnencryptedDb(_T(""), eError::NON_FATAL));
        }


        // open the report...
        cFCOReport       report;
        cFCOReportHeader reportHeader;
        if (!mpData->mpReport)
        {
            cTWUtil::ReadReport(mpData->mReportFile.c_str(),
                                reportHeader,
                                report,
                                localKeyfile.GetPublicKey(),
                                false,
                                bReportEncrypted);
            mpData->mpReport       = &report;
            mpData->mpReportHeader = &reportHeader;
        }
        else
            ASSERT(mpData->mpReportHeader != 0);

        // open the private key for "early passphrase"
        // I don't think that early passphrase should apply to db update; there is no reason the user
        // would want to enter the passphrase before editing the report .. mdb
        // TODO: Well ok, can we remove this commented out code then?
        const cElGamalSigPrivateKey* pPrivateKey = 0;
        /*
      if(bDbEncrypted && (! mpData->mbLatePassphrase))
      {
         pPrivateKey = cTWUtil::CreatePrivateKey(localKeyfile, mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0);
      }
      */

        // have the user modify the report, if they want to
        if (mpData->mbInteractive)
        {
            // put a try/catch here to allow ourselves to print a message
            // saying the editor launch failed
            try
            {
                cTextReportViewer trv(*mpData->mpReportHeader, *mpData->mpReport);
                trv.DisplayReportAndHaveUserUpdateIt(mpData->mEditor, cTextReportViewer::FULL_REPORT);
            }
            catch (...)
            {
                TCERR << TSS_GetString(cTripwire, tripwire::STR_ERR_UPDATE_ED_LAUNCH) << std::endl;
                throw;
            }
        }

        // if the report is empty, there is no need to do the update...
        if (!cTWCmdLineUtil::ReportContainsFCO(*mpData->mpReport))
        {
            // tell them ...
            iUserNotify::GetInstance()->Notify(1, TSS_GetString(cTripwire, tripwire::STR_REPORT_EMPTY).c_str());
            // keep those pesky ASSERTs from cKeyFile away!
            if (pPrivateKey)
                localKeyfile.ReleasePrivateKey();
            return 0; // is this the right return val?
        }

        // give a stern warning if the database is not encrypted
        if (!bDbEncrypted && iUserNotify::GetInstance()->GetVerboseLevel() > iUserNotify::V_SILENT)
        {
            // warn if the database is not encrytped
            cTWUtil::PrintErrorMsg(eTWDbNotEncrypted(_T(""), eError::NON_FATAL | eError::SUPRESS_THIRD_MSG));
        }

        // iterate over all the genres in the report...
        //
        cFCOReportGenreIter genreIter(*mpData->mpReport);
        for (genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next())
        {
            cGenreSwitcher::GetInstance()->SelectGenre(genreIter.GetGenre());
            //
            // seek the db iter to the current genre...
            //
            cFCODatabaseFile::iterator dbIter(*mpData->mpDbFile);
            dbIter.SeekToGenre(genreIter.GetGenre());
            if (dbIter.Done())
            {
                //TODO -- what is the right thing to do here?
                //
                ASSERT(false);
                throw eTWDbDoesntHaveGenre();
            }
            //
            // actually do the integrity check...
            //
            uint32 udFlags = 0;
            udFlags |= (mpData->mbResetAccessTime ? cUpdateDb::FLAG_ERASE_FOOTPRINTS_UD : 0);

            cUpdateDb update(dbIter.GetDb(), *mpData->mpReport, pQueue);
            if ((!update.Execute(udFlags)) && mpData->mbSecureMode)
            {
                // we will not perform the update; simply exit.
                TCOUT << TSS_GetString(cTripwire, tripwire::STR_DB_NOT_UPDATED) << std::endl;
                return 8;
            }

            //
            // update the header info for the database...
            //
            // merge the prop displayers...
            dbIter.GetGenreHeader().GetPropDisplayer()->Merge(genreIter.GetGenreHeader().GetPropDisplayer());
        }

        // Update the header
        // Note: We DO NOT call cFCODatabaseUtil::CalculateHeader() here.  We only
        // want to update these entries.
        mpData->mpDbFile->GetHeader().SetCreator(cTWUtil::GetCurrentUser());
        mpData->mpDbFile->GetHeader().SetLastDBUpdateTime(cSystemInfo::GetExeStartTime());

        //
        // write the db to disk...
        //
        if (bDbEncrypted)
        {
            // open the private key for "late prompting"
            if (!pPrivateKey)
            {
                pPrivateKey = cTWUtil::CreatePrivateKey(
                    localKeyfile, mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0, cTWUtil::KEY_LOCAL);
            }

            // backup the file we are about to overwrite
            cFileUtil::BackupFile(mpData->mDbFile);

            cTWUtil::WriteDatabase(mpData->mDbFile.c_str(), *mpData->mpDbFile, true, pPrivateKey);
            localKeyfile.ReleasePrivateKey();
        }
        else
        {
            cFileUtil::BackupFile(mpData->mDbFile); // backup the file we are about to overwrite
            cTWUtil::WriteDatabase(
                mpData->mDbFile.c_str(), *mpData->mpDbFile, false, NULL); // false means no encryption
        }
    }
    catch (eError& e)
    {
        if (mpData->mbLogToSyslog)
        {
            TSTRING msg = TSS_GetString(cTripwire, tripwire::STR_SYSLOG_UP_FAIL_MSG);
            msg.append(cErrorTable::GetInstance()->Get(e.GetID()));

            cSyslog::Log(TSS_GetString(cTripwire, tripwire::STR_SYSLOG_IDENT).c_str(), cSyslog::LOG_ERROR, msg.c_str());
        }

        cTWUtil::PrintErrorMsg(e);
        return 8;
    }

    if (mpData->mbLogToSyslog)
    {
        TSTRING msg = TSS_GetString(cTripwire, tripwire::STR_SYSLOG_UP_MSG);
        msg.append(mpData->mDbFile);
        cSyslog::Log(TSS_GetString(cTripwire, tripwire::STR_SYSLOG_IDENT).c_str(), cSyslog::LOG_SUCCESS, msg.c_str());
    }

    // everything went ok; return 0
    return 0;
}

///////////////////////////////////////////////////////////////////////////////
// GetModeUsage
///////////////////////////////////////////////////////////////////////////////
TSTRING cTWModeDbUpdate::GetModeUsage(void)
{
    return (TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_UPDATE));
}

//#############################################################################
// cTWModePolUpdate
//#############################################################################
class cTWModePolUpdate_i : public cTWModeCommon
{
public:
    TSTRING     mTextPolFile;
    wc16_string mSitePassphrase;
    bool        mSiteProvided;
    bool        mbSecureMode;

    // ctor can set up some default values
    cTWModePolUpdate_i() : cTWModeCommon(), mSiteProvided(false), mbSecureMode(true)
    {
    }
};

///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModePolUpdate::cTWModePolUpdate()
{
    mpData = new cTWModePolUpdate_i;
}

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

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModePolUpdate::InitCmdLineParser(cCmdLineParser& cmdLine)
{
    // initialize the switches common to all modes...
    InitCmdLineCommon(cmdLine);

    cmdLine.AddArg(
        cTWCmdLine::MODE_UPDATE_POL, TSTRING(_T("")), TSTRING(_T("update-policy")), cCmdLineParser::PARAM_NONE);
    cmdLine.AddArg(cTWCmdLine::TEXT_POL_FILE, TSTRING(_T("")), TSTRING(_T("")), cCmdLineParser::PARAM_ONE);
    cmdLine.AddArg(
        cTWCmdLine::LOCAL_PASSPHRASE, TSTRING(_T("P")), TSTRING(_T("local-passphrase")), cCmdLineParser::PARAM_ONE);
    cmdLine.AddArg(
        cTWCmdLine::SITE_PASSPHRASE, TSTRING(_T("Q")), TSTRING(_T("site-passphrase")), cCmdLineParser::PARAM_ONE);
    cmdLine.AddArg(cTWCmdLine::SECURE_MODE, TSTRING(_T("Z")), TSTRING(_T("secure-mode")), cCmdLineParser::PARAM_ONE);
}

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModePolUpdate::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
    // first, fill out everything with the config file info...
    FillOutConfigInfo(mpData, cf);

    // 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())
    {
        TSTRING fullPath;
        switch (iter.ArgId())
        {
        case cTWCmdLine::TEXT_POL_FILE:
            ASSERT(iter.NumParams() > 0);
            mpData->mTextPolFile = iter.ParamAt(0);
            //
            // convert it to a full path
            //
            if (iFSServices::GetInstance()->FullPath(fullPath, mpData->mTextPolFile))
                mpData->mTextPolFile = fullPath;
            break;
        case cTWCmdLine::SITE_PASSPHRASE:
            ASSERT(iter.NumParams() > 0);
            mpData->mSitePassphrase = cStringUtil::TstrToWstr(iter.ParamAt(0));
            mpData->mSiteProvided   = true;
            break;
        case cTWCmdLine::SECURE_MODE:
            ASSERT(iter.NumParams() > 0);
            if (iter.ParamAt(0).compare(_T("high")) == 0)
                mpData->mbSecureMode = true;
            else if (iter.ParamAt(0).compare(_T("low")) == 0)
                mpData->mbSecureMode = false;
            else
            {
                // invalid parameter to secure mode  switch...
                // TODO -- print this to stderr; how do I display (1) the switch name
                // and (2) the possible values?
                // TODO -- move {high, low} somewhere else
                TCERR << TSS_GetString(cTripwire, tripwire::STR_ERR_BAD_PARAM) << _T(" : ") << iter.ParamAt(0).c_str()
                      << _T("; must be {high, low}") << std::endl;
                return false;
            }
            break;
        default:
            // should I do anything, or just ignore this?
            ;
        }
    }

    //----------------------------------------
    // I require the following information:
    //
    // * local key file
    // * site key file
    // * policy file
    // * db file
    //-----------------------------------------
    TEST_INIT_REQUIREMENT((!mpData->mLocalKeyFile.empty()), cTW, tw::STR_ERR_MISSING_LOCAL_KEY);
    TEST_INIT_REQUIREMENT((!mpData->mSiteKeyFile.empty()), cTW, tw::STR_ERR_MISSING_SITE_KEY);
    TEST_INIT_REQUIREMENT((!mpData->mPolFile.empty()), cTW, tw::STR_ERR_MISSING_POLICY);
    TEST_INIT_REQUIREMENT((!mpData->mDbFile.empty()), cTW, tw::STR_ERR_MISSING_DB);

    // if a passphrase was passed on the command line, then late prompting should
    // be set to false so that wrong passwords are detected immediately
    //
    // Note that the local passphrase was already checked against in FillOutCmdLineInfo(),
    // so I only need to test the site passphrase
    if (mpData->mSiteProvided && mpData->mbLatePassphrase)
    {
        // TODO -- I don't know if we should display this or not -- mdb
        //iUserNotify::GetInstance()->Notify(  iUserNotify::V_VERBOSE, TSS_GetString( cTripwire, tripwire::STR_NOTIFY_CHANGE_PROMPT_TIME).c_str() );
        mpData->mbLatePassphrase = false;
    }

    // make sure that the config file and site key file are in sync...
    //
    if (cTWUtil::VerifyCfgSiteKey(mstrConfigFile, mpData->mSiteKeyFile) == false)
        cTWUtil::PrintErrorMsg(eTWCfgUnencrypted(_T(""), eError::NON_FATAL | eError::SUPRESS_THIRD_MSG));

    // Set the cross file systems flag appropriately.
    cFSDataSourceIter::SetFileSystemCrossing(mpData->mbCrossFileSystems);
    return true;
}

///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWModePolUpdate::Execute(cErrorQueue* pQueue)
{
    try
    {
        // make sure all of the files are accessable
        cFileUtil::TestFileWritable(mpData->mPolFile);
        cFileUtil::TestFileWritable(mpData->mDbFile);

        // open and parse the new policy file...
        cGenreSpecListVector genreSpecList;
        cTWCmdLineUtil::ParseTextPolicyFile(genreSpecList, mpData->mTextPolFile, mpData->mSiteKeyFile, pQueue);

        cFCODatabaseFile dbFile;
        cKeyFile         localKeyfile, siteKeyfile;
        cPrivateKeyProxy privateLocal, privateSite;
        cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);
        cTWUtil::OpenKeyFile(siteKeyfile, mpData->mSiteKeyFile);

        // open the old database; note that ReadDatabase will set bEncrypted and open the
        // key file if necessary
        bool bDbEncrypted;
        bool bPolEncrypted = true;

        cTWUtil::ReadDatabase(mpData->mDbFile.c_str(), dbFile, localKeyfile.GetPublicKey(), bDbEncrypted);

        //
        // give a warning if the user that created the database is not the same as the user running right now...
        //
        TSTRING userName;
        if (iFSServices::GetInstance()->GetCurrentUserName(userName))
        {
            if (dbFile.GetHeader().GetCreator().compare(userName.c_str()) != 0)
            {
                // TODO -- move these strings to the string table
                TOSTRINGSTREAM str;
                str << TSS_GetString(cTripwire, tripwire::STR_ERR2_DIFFERENT_USERS1) << dbFile.GetHeader().GetCreator()
                    << TSS_GetString(cTripwire, tripwire::STR_ERR2_DIFFERENT_USERS2) << userName << std::ends;
                cTWUtil::PrintErrorMsg(eICDifferentUsers(str.str(), eError::NON_FATAL));
            }
        }

        // warn if the database is not encrytped
        if (!bDbEncrypted && iUserNotify::GetInstance()->GetVerboseLevel() > iUserNotify::V_SILENT)
        {
            cTWUtil::PrintErrorMsg(eTWDbNotEncrypted(_T(""), eError::NON_FATAL | eError::SUPRESS_THIRD_MSG));
        }

        if ((!bDbEncrypted) && (mpData->mLocalProvided))
        {
            //Issue a warning if a passphrase has been supplied, and the database is NOT encrypted.
            cTWUtil::PrintErrorMsg(eTWPassForUnencryptedDb(_T(""), eError::NON_FATAL));
        }


        try
        {
            cFileManipulator polManipulator(mpData->mPolFile.c_str());
            polManipulator.Init();
            bPolEncrypted = polManipulator.GetEncoding() == cFileHeader::ASYM_ENCRYPTION;
        }
        catch (eError&)
        {
        }

        //
        // get the passwords for early prompting
        //
        if (!mpData->mbLatePassphrase)
        {
            if (bDbEncrypted)
                cTWUtil::CreatePrivateKey(privateLocal,
                                          localKeyfile,
                                          mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0,
                                          cTWUtil::KEY_LOCAL);

            if (bPolEncrypted)
                cTWUtil::CreatePrivateKey(privateSite,
                                          siteKeyfile,
                                          mpData->mSiteProvided ? mpData->mSitePassphrase.c_str() : 0,
                                          cTWUtil::KEY_SITE);
        }


        /////////////////////////////////////////////////////////
        // iterate through all the genres in the new policy...
        /////////////////////////////////////////////////////////
        cFCODatabaseFile::iterator     dbIter(dbFile);
        cGenreSpecListVector::iterator genreIter;
        for (genreIter = genreSpecList.begin(); genreIter != genreSpecList.end(); ++genreIter)
        {
            // make this genre the current genre.
            //
            cGenreSwitcher::GetInstance()->SelectGenre(genreIter->GetGenre());
            //
            // seek the database iter to this genre...
            //
            dbIter.SeekToGenre(genreIter->GetGenre());
            if (!dbIter.Done())
            {
                // perform the policy update
                //
                cPolicyUpdate pu(
                    genreIter->GetGenre(), dbIter.GetSpecList(), genreIter->GetSpecList(), dbIter.GetDb(), pQueue);
                uint32 puFlags = 0;
                puFlags |= mpData->mbSecureMode ? cPolicyUpdate::FLAG_SECURE_MODE : 0;
                puFlags |= (mpData->mbResetAccessTime ? cPolicyUpdate::FLAG_ERASE_FOOTPRINTS_PU : 0);
                puFlags |= (mpData->mbDirectIO ? cPolicyUpdate::FLAG_DIRECT_IO : 0);

                if ((!pu.Execute(puFlags)) && (mpData->mbSecureMode))
                {
                    // they were in secure mode and errors occured; an error condition
                    TCOUT << TSS_GetString(cTripwire, tripwire::STR_ERR_POL_UPDATE) << std::endl;
                    return 8;
                }
                // we need to update the database's prop set...
                //
                dbIter.GetSpecList() = genreIter->GetSpecList();
                //
                // TODO -- do some header stuff here?
                //
            }
            else
            {
                // do a database init for this genre; it did not appear in the old policy...
                //
                TW_NOTIFY_NORMAL(TSS_GetString(cTripwire, tripwire::STR_PU_ADDING_GENRE).c_str(),
                                 cGenreSwitcher::GetInstance()->GenreToString(genreIter->GetGenre(), true));
                dbFile.AddGenre(genreIter->GetGenre(), &dbIter);

                // copy the spec list to the database...
                dbIter.GetSpecList() = genreIter->GetSpecList();

                // generate the database...
                // TODO -- turn pQueue into an error bucket

                uint32 gdbFlags = 0;
                gdbFlags |= (mpData->mbResetAccessTime ? cGenerateDb::FLAG_ERASE_FOOTPRINTS_GD : 0);
                gdbFlags |= (mpData->mbDirectIO ? cGenerateDb::FLAG_DIRECT_IO : 0);

                cGenerateDb::Execute(
                    dbIter.GetSpecList(), dbIter.GetDb(), dbIter.GetGenreHeader().GetPropDisplayer(), pQueue, gdbFlags);

                //TODO -- what other prop displayer stuff do I have to do here?
            }
        }
        //////////////////////////////////////////////////////////////////////////////////////
        // now, we need to prune genres that are in the database but not in the new policy...
        //////////////////////////////////////////////////////////////////////////////////////
        for (dbIter.SeekBegin(); !dbIter.Done();)
        {
            bool bFoundGenre = false;
            for (genreIter = genreSpecList.begin(); genreIter != genreSpecList.end(); ++genreIter)
            {
                if (genreIter->GetGenre() == dbIter.GetGenre())
                {
                    bFoundGenre = true;
                    break;
                }
            }
            if (!bFoundGenre)
            {
                // we need to remove this genre from the db...
                //
                dbIter.Remove();
            }
            else
                dbIter.Next();
        }


        // let's go ahead and update the database and policy file...

        //
        // open the site key file and get the site passphrase
        //
        if (!privateSite.Valid() && bPolEncrypted)
            cTWUtil::CreatePrivateKey(privateSite,
                                      siteKeyfile,
                                      mpData->mSiteProvided ? mpData->mSitePassphrase.c_str() : 0,
                                      cTWUtil::KEY_SITE);
        //
        // get the passphrase for the database, if necessary (it is important that we get both
        // private keys before saving anything
        //
        if (bDbEncrypted && !privateLocal.Valid())
        {
            ASSERT(mpData->mbLatePassphrase);
            cTWUtil::CreatePrivateKey(privateLocal,
                                      localKeyfile,
                                      mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0,
                                      cTWUtil::KEY_LOCAL);
        }

        // backup the file we are about to overwrite
        cFileUtil::BackupFile(mpData->mPolFile);

        //
        // write the policy file
        //
        cTWUtil::UpdatePolicyFile(mpData->mPolFile.c_str(),
                                  mpData->mTextPolFile.c_str(),
                                  bPolEncrypted,
                                  bPolEncrypted ? privateSite.GetKey() : 0);

        //
        // update the header info
        //
        // Note: We DO NOT call cFCODatabaseUtil::CalculateHeader() here.  We only
        // want to update these entries.
        dbFile.GetHeader().SetCreator(cTWUtil::GetCurrentUser());
        dbFile.GetHeader().SetPolicyFilename(mpData->mPolFile);
        dbFile.GetHeader().SetConfigFilename(mstrConfigFile);
        dbFile.GetHeader().SetLastDBUpdateTime(cSystemInfo::GetExeStartTime());

        // backup the file we are about to overwrite
        cFileUtil::BackupFile(mpData->mDbFile);

        //
        // write the db to disk...
        //
        cTWUtil::WriteDatabase(mpData->mDbFile.c_str(), dbFile, bDbEncrypted, bDbEncrypted ? privateLocal.GetKey() : 0);
    }
    catch (eError& e)
    {
        if (mpData->mbLogToSyslog)
        {
            TSTRING msg = TSS_GetString(cTripwire, tripwire::STR_SYSLOG_POLUP_FAIL_MSG);
            msg.append(cErrorTable::GetInstance()->Get(e.GetID()));

            cSyslog::Log(TSS_GetString(cTripwire, tripwire::STR_SYSLOG_IDENT).c_str(), cSyslog::LOG_ERROR, msg.c_str());
        }

        cTWUtil::PrintErrorMsg(e);
        return 8;
    }

    if (mpData->mbLogToSyslog)
    {
        TSTRING msg = TSS_GetString(cTripwire, tripwire::STR_SYSLOG_POLUP_MSG);
        msg.append(mpData->mDbFile);
        cSyslog::Log(TSS_GetString(cTripwire, tripwire::STR_SYSLOG_IDENT).c_str(), cSyslog::LOG_SUCCESS, msg.c_str());
    }

    return 0;
}

///////////////////////////////////////////////////////////////////////////////
// GetModeUsage
///////////////////////////////////////////////////////////////////////////////
TSTRING cTWModePolUpdate::GetModeUsage(void)
{
    return (TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_UPDATE_POLICY));
}

//#############################################################################
// cTWModeTest
//#############################################################################

class cTWModeTest_i : public cTWModeCommon
{
public:
    cTWModeTest_i() : cTWModeCommon()
    {
        mMode = TEST_INVALID;
    }

    enum TestMode
    {
        TEST_INVALID,
        TEST_EMAIL
    };

    TestMode mMode;
    TSTRING  mEmailAddress;
};

///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeTest::cTWModeTest()
{
    mpData = new cTWModeTest_i;
}

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

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeTest::InitCmdLineParser(cCmdLineParser& cmdLine)
{
    // initialize the switches common to all modes...
    InitCmdLineCommon(cmdLine);

    // The only allowed test mode syntax so far is
    // "tripwire --test --email name@domain.com" or "tripwire -t -e name@domain.com".
    cmdLine.AddArg(cTWCmdLine::MODE_TEST, TSTRING(_T("")), TSTRING(_T("test")), cCmdLineParser::PARAM_NONE);
    cmdLine.AddArg(cTWCmdLine::TEST_EMAIL, TSTRING(_T("e")), TSTRING(_T("email")), cCmdLineParser::PARAM_ONE);

    cmdLine.AddDependency(cTWCmdLine::MODE_TEST, cTWCmdLine::TEST_EMAIL);
}

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeTest::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
    // first, fill out everything with the config file info...
    FillOutConfigInfo(mpData, cf);

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

    cCmdLineIter iter(cmdLine);
    for (iter.SeekBegin(); !iter.Done(); iter.Next())
    {
        switch (iter.ArgId())
        {
        case cTWCmdLine::TEST_EMAIL:
            mpData->mMode         = cTWModeTest_i::TEST_EMAIL;
            mpData->mEmailAddress = iter.ParamAt(0);
            break;
        default:
            // should I do anything, or just ignore this?
            ;
        }
    }

    // make sure that we have a valid operation
    TEST_INIT_REQUIREMENT((mpData->mMode != cTWModeTest_i::TEST_INVALID), cTripwire, tripwire::STR_ERR_NO_TEST_MODE);

    if (mpData->mMode == cTWModeTest_i::TEST_EMAIL)
    {
        // make sure that we have a valid mail method
        TEST_INIT_REQUIREMENT(
            (cMailMessage::NO_METHOD != mpData->mMailMethod), cTripwire, tripwire::STR_ERR_NO_MAIL_METHOD);
        TEST_INIT_REQUIREMENT(
            (cMailMessage::INVALID_METHOD != mpData->mMailMethod), cTripwire, tripwire::STR_ERR_INVALID_MAIL_METHOD);
    }

    return true;
}

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

    switch (mpData->mMode)
    {
    case cTWModeTest_i::TEST_EMAIL:
    {
        cTWCmdLineUtil::SendEmailTestMessage(mpData->mEmailAddress, mpData);
    }
    default:
        // should I do anything, or just ignore this?
        ;
    }

    return 0;
}

TSTRING cTWModeTest::GetModeUsage()
{
    return (TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_TEST));
}

//#############################################################################
// cTWModeHelp
//#############################################################################

class cTWModeHelp_i : public cTWModeCommon
{
public:
    cTWModeHelp_i()
    {
    }
    ~cTWModeHelp_i()
    {
    }

    std::set<TSTRING> mModes;
    // A set of modes to output usage statements for a specific mode.
    std::set<TSTRING> mPrinted;
    // A set for making sure we don't print the same help twice.
};


///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeHelp::cTWModeHelp()
{
    mpData = new cTWModeHelp_i();
}

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

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeHelp::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(cTWCmdLine::MODE, TSTRING(_T("m")), TSTRING(_T("")), cCmdLineParser::PARAM_MANY, true);
    cmdLine.AddArg(cTWCmdLine::MODE_HELP, TSTRING(_T("?")), TSTRING(_T("help")), cCmdLineParser::PARAM_MANY);
    cmdLine.AddArg(cTWCmdLine::MODE_HELP_ALL, TSTRING(_T("")), TSTRING(_T("all")), cCmdLineParser::PARAM_MANY);
    cmdLine.AddArg(cTWCmdLine::MODE_INIT, TSTRING(_T("i")), TSTRING(_T("init")), cCmdLineParser::PARAM_MANY);
    cmdLine.AddArg(cTWCmdLine::MODE_CHECK, TSTRING(_T("c")), TSTRING(_T("check")), cCmdLineParser::PARAM_MANY);
    cmdLine.AddArg(cTWCmdLine::MODE_UPDATE_DB, TSTRING(_T("u")), TSTRING(_T("update")), cCmdLineParser::PARAM_MANY);
    cmdLine.AddArg(
        cTWCmdLine::MODE_UPDATE_POL, TSTRING(_T("p")), TSTRING(_T("update-policy")), cCmdLineParser::PARAM_MANY);
    cmdLine.AddArg(cTWCmdLine::MODE_TEST, TSTRING(_T("t")), TSTRING(_T("test")), cCmdLineParser::PARAM_MANY);
    cmdLine.AddArg(cTWCmdLine::MODE_EXPLORE, TSTRING(_T("")), TSTRING(_T("explore")), cCmdLineParser::PARAM_MANY);
    cmdLine.AddArg(cTWCmdLine::MODE_DEBUG, TSTRING(_T("")), TSTRING(_T("verify")), cCmdLineParser::PARAM_MANY);
}

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeHelp::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 cTWCmdLine::MODE_HELP:
        {
            int i;
            for (i = 0; i < iter.NumParams(); ++i)
            {
                mpData->mModes.insert(iter.ParamAt(i));
            }
        }
        break;
        case cTWCmdLine::MODE:
        {
            int i;
            for (i = 0; i < iter.NumParams(); ++i)
            {
                mpData->mModes.insert(iter.ParamAt(i));
            }
        }
        break;
        // allow users to enter the mode
        // names with "--" prepended.  We have to do this, since
        // the cmdlineparser treats them as switches.
        case cTWCmdLine::MODE_HELP_ALL:   // fall through
        case cTWCmdLine::MODE_INIT:       // fall through
        case cTWCmdLine::MODE_CHECK:      // fall through
        case cTWCmdLine::MODE_UPDATE_DB:  // fall through
        case cTWCmdLine::MODE_UPDATE_POL: // fall through
        case cTWCmdLine::MODE_TEST:       // fall through
        case cTWCmdLine::MODE_EXPLORE:    // fall through
        case cTWCmdLine::MODE_DEBUG:
        {
            int i;
            // push back the parameter that was actually passed.
            TSTRING str = iter.ActualParam();
            // Kill off the initial "--" or "-"
            str.erase(0, 1);
            if (str.length() != 1)
                str.erase(0, 1);

            mpData->mModes.insert(str);
            for (i = 0; i < iter.NumParams(); ++i)
                mpData->mModes.insert(iter.ParamAt(i));
        }
        break;
        default:
            // should I do anything, or just ignore this?
            // cmdlineparser will catch the bad args.
            ;
        }
    }

    return true;
}

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

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

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

    if (it == mpData->mModes.end()) // all that was passed was --help
    {
        // Output a short usage summary for each mode.
        TCOUT << TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_USAGE_SUMMARY);
        //
        // That's it, return.
        return 8;
    }

    for (; it != mpData->mModes.end(); ++it)
    {
        if (_tcscmp((*it).c_str(), _T("all")) == 0)
        {
            //Since --help all was passed, emit all help messages and return.
            TCOUT << TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_INIT);
            TCOUT << TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_CHECK);
            TCOUT << TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_UPDATE);
            TCOUT << TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_UPDATE_POLICY);
            TCOUT << TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_TEST);
#ifdef DEBUG
            // TODO: Do we need help messages for these modes? DRA
#endif
            //We're done, return
            return 8;
        }
    }

    //We need some subset of the usage statements.  Figure out which modes have
    //been specified:
    it = mpData->mModes.begin();
    for (; it != mpData->mModes.end(); ++it)
    {
        // This is going to be ugly code...
        if (_tcscmp((*it).c_str(), _T("init")) == 0 || _tcscmp((*it).c_str(), _T("i")) == 0)
        {
            //make sure we don't print the same help twice...
            if (mpData->mPrinted.find(_T("init")) == mpData->mPrinted.end())
            {
                TCOUT << TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_INIT);
                mpData->mPrinted.insert(_T("init"));
            }
        }
        else if (_tcscmp((*it).c_str(), _T("check")) == 0 || _tcscmp((*it).c_str(), _T("c")) == 0)
        {
            if (mpData->mPrinted.find(_T("check")) == mpData->mPrinted.end())
            {
                TCOUT << TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_CHECK);
                mpData->mPrinted.insert(_T("check"));
            }
        }
        else if (_tcscmp((*it).c_str(), _T("update")) == 0 || _tcscmp((*it).c_str(), _T("u")) == 0)
        {
            if (mpData->mPrinted.find(_T("update")) == mpData->mPrinted.end())
            {
                TCOUT << TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_UPDATE);
                mpData->mPrinted.insert(_T("update"));
            }
        }
        else if (_tcscmp((*it).c_str(), _T("update-policy")) == 0 || _tcscmp((*it).c_str(), _T("p")) == 0)
        {
            if (mpData->mPrinted.find(_T("update-policy")) == mpData->mPrinted.end())
            {
                TCOUT << TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_UPDATE_POLICY);
                mpData->mPrinted.insert(_T("update-policy"));
            }
        }
        else if (_tcscmp((*it).c_str(), _T("test")) == 0 || _tcscmp((*it).c_str(), _T("t")) == 0)
        {
            if (mpData->mPrinted.find(_T("test")) == mpData->mPrinted.end())
            {
                TCOUT << TSS_GetString(cTripwire, tripwire::STR_TRIPWIRE_HELP_TEST);
                mpData->mPrinted.insert(_T("test"));
            }
        }
        else if (_tcscmp((*it).c_str(), _T("explore")) == 0)
            TCOUT << _T(""); //we currently don't have usage statements for this mode.
        else if (_tcscmp((*it).c_str(), _T("verify")) == 0)
            TCOUT << _T(""); //we currently don't have usage statements for this mode.
        else
        {
            cTWUtil::PrintErrorMsg(eTWHelpInvalidModeSwitch((*it), eError::NON_FATAL));
            TCOUT << std::endl;
            // emit error string, this mode does not exist
        }
    }

    //Everything went okay
    return 8;
}

//================================================================
// UTIL FUNCTIONS
//================================================================

bool util_GetEditor(TSTRING& strEd)
{
    // see if VISUAL environment var is set
    TCHAR* pchEd = _tgetenv(_T("VISUAL"));
    if (pchEd != NULL && pchEd[0] != _T('\0'))
    {
        strEd = pchEd;
        return true;
    }

    // see if EDITOR environment var is set
    pchEd = _tgetenv(_T("EDITOR"));
    if (pchEd != NULL && pchEd[0] != _T('\0'))
    {
        strEd = pchEd;
        return true;
    }

    return false;
}

//Encase the explore and debug modes in this ifdef, since they are for internal
//use only.
#ifdef DEBUG

//#############################################################################
// cTWModeExploreDb
//#############################################################################
#include "tw/dbexplore.h"

class cTWModeExploreDb_i : public cTWModeCommon
{
public:
    cTWModeExploreDb_i() : cTWModeCommon()
    {
    }
};

///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeExploreDb::cTWModeExploreDb()
{
    mpData = new cTWModeExploreDb_i;
}

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

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeExploreDb::InitCmdLineParser(cCmdLineParser& cmdLine)
{
    // initialize the switches common to all modes...
    InitCmdLineCommon(cmdLine);

    cmdLine.AddArg(cTWCmdLine::MODE_EXPLORE, TSTRING(_T("")), TSTRING(_T("explore")), cCmdLineParser::PARAM_NONE);
}

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeExploreDb::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
    // first, fill out everything with the config file info...
    FillOutConfigInfo(mpData, cf);

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

    return true;
}

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

    cFCODatabaseFile           dbFile;
    cFCODatabaseFile::iterator dbIter(dbFile);
    try
    {
        cKeyFile localKeyfile;
        cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);

        bool bEncrypted;
        cTWUtil::ReadDatabase(mpData->mDbFile.c_str(), dbFile, localKeyfile.GetPublicKey(), bEncrypted);
        //
        // seek to the first genre in the db...
        //
        dbIter.SeekBegin();
        if (dbIter.Done())
        {
            //TODO -- what is the right thing to do here?
            //
            ASSERT(false);
            throw eTWDbHasNoGenres();
        }
        //
        // ok, time to explore the database!
        //
        cDbExplore::Execute(dbIter);
    }
    catch (eError& e)
    {
        cTWUtil::PrintErrorMsg(e);
        return 8;
    }

    return 0;
}

//#############################################################################
// cTWModeDebugDb
//#############################################################################
#include "tw/dbdebug.h"

class cTWModeDebugDb_i : public cTWModeCommon
{
public:
    cTWModeDebugDb_i() : cTWModeCommon()
    {
    }
};

///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeDebugDb::cTWModeDebugDb()
{
    mpData = new cTWModeDebugDb_i;
}

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

///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeDebugDb::InitCmdLineParser(cCmdLineParser& cmdLine)
{
    // initialize the switches common to all modes...
    InitCmdLineCommon(cmdLine);

    cmdLine.AddArg(cTWCmdLine::MODE_DEBUG, TSTRING(_T("")), TSTRING(_T("verifydb")), cCmdLineParser::PARAM_NONE);
}

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeDebugDb::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
    // first, fill out everything with the config file info...
    FillOutConfigInfo(mpData, cf);

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

    return true;
}

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

    cFCODatabaseFile           dbFile;
    cFCODatabaseFile::iterator dbIter(dbFile);
    try
    {
        cKeyFile localKeyfile;
        cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);

        bool bEncrypted;
        cTWUtil::ReadDatabase(mpData->mDbFile.c_str(), dbFile, localKeyfile.GetPublicKey(), bEncrypted);
        //
        // seek to the first genre in the db...
        //
        dbIter.SeekBegin();
        if (dbIter.Done())
        {
            //TODO -- what is the right thing to do here?
            //
            ASSERT(false);
            throw eTWDbHasNoGenres();
        }
        //
        // debug the database.
        //
        cDbDebug::Execute(dbIter, mpData->mDbFile);
    }
    catch (eError& e)
    {
        cTWUtil::PrintErrorMsg(e);
        return 8;
    }

    return 0;
}


#endif //DEBUG
