tripwire-open-source/src/tw/textreportviewer.cpp

2023 lines
62 KiB
C++

//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights
// reserved.
//
// This program is free software. The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version. You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
//
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE. Please see the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
//
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// textreportviewer.cpp -- implementation for textreportviewer class
//
// TODO:BAM put strings into resources
// TODO:BAM add debug strings
//=========================================================================
// DIRECTIVES
//=========================================================================
//=========================================================================
// INCLUDES
//=========================================================================
#include "stdtw.h"
#include "textreportviewer.h"
#include "core/errorbucketimpl.h"
#include "core/fsservices.h"
#include "core/timeconvert.h"
#include "core/msystem.h"
#include "core/twlocale.h"
#include "core/errortable.h"
#include "core/debug.h"
#include "core/errorutil.h"
#include "core/displayutil.h"
#include "core/fixedfilebuf.h"
#include "fco/fcospec.h"
#include "fco/iterproxy.h"
#include "fco/fconame.h"
#include "fco/fcopropvector.h"
#include "fco/fcopropset.h"
#include "fco/fcoprop.h"
#include "fco/fconametranslator.h"
#include "fco/fcoundefprop.h"
#include "fco/genreswitcher.h"
#include "fco/twfactory.h"
#include "fco/fcopropdisplayer.h"
#include "fco/fcospecattr.h"
#include "fco/fcoundefprop.h"
#include "core/stringutil.h"
#include "util/fileutil.h"
#include "headerinfo.h"
#include "fcoreport.h"
#include "twstrings.h"
#include "core/displayencoder.h"
//=========================================================================
// STANDARD LIBRARY INCLUDES
//=========================================================================
#include <fstream>
#include <iomanip>
#include <time.h>
#include <fcntl.h>
#ifdef HAVE_WCHAR_H
# include <wchar.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
//=========================================================================
// OTHER DIRECTIVES
//=========================================================================
using namespace std;
//=========================================================================
// GLOBALS
//=========================================================================
static const TCHAR* g_sz79Dashes = _T("-------------------------------------------------------------------------------");
static const TCHAR* g_sz40Dashes = _T("----------------------------------------");
static const TCHAR* g_sz79Equals = _T("===============================================================================");
//=========================================================================
// UTIL FUNCTION PROTOTYES
//=========================================================================
static void OpenOutputFile( fixed_basic_ofstream<TCHAR>& out, const TSTRING& strFile ); // throw( eTextReportViewer )
static void OpenInputFile ( std::ifstream& out, const TSTRING& strFile ); // throw( eTextReportViewer )
static bool PrintableProp( const iFCO* pfcoOld, const iFCO* pfcoNew, int j );
static bool SpecEmpty( const cFCOReportSpecIter& ri );
static TSTRING util_Encode( const TSTRING& sIn )
{
static cDisplayEncoder e;
TSTRING sOut = sIn;
e.Encode( sOut );
return sOut;
}
//=========================================================================
// METHOD CODE
//=========================================================================
cTextReportViewer::cTextReportViewer( const cFCOReportHeader& h, const cFCOReport& r )
{
Init( h, const_cast<cFCOReport&>(r) ); // TODO:BAM -- hack to allow creation
// for updating (non-const report) and non-updating
// (const report) in one constructor format
}
cTextReportViewer::cTextReportViewer( const cFCOReportHeader& h, cFCOReport& r )
{
Init( h, r );
}
int cTextReportViewer::Init( const cFCOReportHeader& h, cFCOReport& r )
{
mpHeader = &h;
mpReport = &r;
mpOut = NULL;
mpIn = NULL;
mpCurPD = NULL;
mpCurNT = NULL;
mfUpdate = false;
mMaxSeverityViolated = 0;
mNumberViolations = 0;
mErrorNum = 1;
mReportingLevel = FULL_REPORT;
mfGotNumbers = false;
mCurrentChar[0] = '\0';
mCurrentCharSize = 0;
return 0;
}
cTextReportViewer::~cTextReportViewer()
{
if( mfUpdate )
{
for( GenreList::iterator i = mFCOsRemoveFromReport.begin(); i != mFCOsRemoveFromReport.end(); ++i )
{
ASSERT( i->second != 0 );
i->second->clear();
delete i->second;
}
}
}
int cTextReportViewer::GetMaxSeverityViolated()
{
if( ! mfGotNumbers )
GetReportNumbers();
return mMaxSeverityViolated;
}
int cTextReportViewer::GetNumberViolations()
{
if( ! mfGotNumbers )
GetReportNumbers();
return mNumberViolations;
}
void cTextReportViewer::InitOStream()
{
// align left
(*mpOut).flags( ( (*mpOut).flags() & ~std::ios::adjustfield ) | std::ios::left );
}
void cTextReportViewer::DisplayReportAndHaveUserUpdateIt( const TSTRING& edName, ReportingLevel level ) //throw (eFSServices)
{
if (!CanUpdate())
{
// We should not be calling this for classes derived from cTextReport that can't update
THROW_INTERNAL("textreportviewer.cpp");
}
ASSERT( ! edName.empty() );
//
// get temp filename in which to store user-readable report
//
TSTRING strTempFile;
iFSServices* pFSServices = iFSServices::GetInstance();
ASSERT( pFSServices != 0 );
pFSServices->GetTempDirName( strTempFile );
strTempFile += _T("twtempXXXXXX");
pFSServices->MakeTempFilename( strTempFile );
// this try/catch ensures the temp file was removed...
try
{
// we are updating...
mfUpdate = true;
//
// write report to the temp file. put checks beside each FCO entry.
//
PrintTextReport( strTempFile, level );
//
// find an appropriate editor and launch it on the temp file. the user
// can uncheck any item she doesn't want put back into the database.
//
LaunchEditorOnFile( strTempFile, edName );
//
// read report back in from the temp file and remove any unchecked entries from
// the report
//
ReadTextReport( strTempFile );
//
// remove unchecked elements from remove
//
RemoveFCOsFromReport();
}
catch(...)
{
// delete the temp file, then rethrow
pFSServices->FileDelete( strTempFile );
throw;
}
//
// delete temp file
//
pFSServices->FileDelete( strTempFile );
}
///////////////////////////////////////////////////////////////////////////////
// PrintTextReport
//
// prints a text version of the passed in report to the passed in stream; the
// version that takes a file name is just a convenience function that calls the
// ostream version. '-' can be passed to signify stdout.
///////////////////////////////////////////////////////////////////////////////
void cTextReportViewer::PrintTextReport( const TSTRING& strFilename, ReportingLevel level ) //throw (eTextReportViewer);
{
mReportingLevel = level;
if( strFilename.compare(_T("-")) == 0 )
{
mpOut = &TCOUT;
OutputTextReport();
}
else
{
fixed_basic_ofstream<TCHAR> out;
OpenOutputFile( out, strFilename );
mpOut = &out;
OutputTextReport();
out.close();
}
}
///////////////////////////////////////////////////////////////////////////////
// PrintTextReport
///////////////////////////////////////////////////////////////////////////////
void cTextReportViewer::PrintTextReport( TOSTREAM& ostr, ReportingLevel level )
{
mReportingLevel = level;
mpOut = &ostr;
OutputTextReport();
}
void cTextReportViewer::GetReportNumbers()
{
cFCOReportGenreIter genreIter( *mpReport );
for( genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
{
// We need to at least get the max severity in this function
cFCOReportSpecIter ri( genreIter );
for( ri.SeekBegin(); !ri.Done(); ri.Next() )
{
if( !IgnoreThisSpec( ri.GetAttr() ) )
{
int nAdded = ri.GetAddedSet()->Size();
int nRemoved = ri.GetRemovedSet()->Size();
int nChanged = ri.GetNumChanged();
if( nAdded || nRemoved || nChanged )
{
// update the max severity
if( mMaxSeverityViolated < ri.GetAttr()->GetSeverity() )
mMaxSeverityViolated = ri.GetAttr()->GetSeverity();
mNumberViolations += ( nAdded + nRemoved + nChanged );
}
}
}
}
mfGotNumbers = true;
}
void cTextReportViewer::OutputTextReport() //throw (eTextReportViewer);
{
ASSERT( mpOut != 0 );
InitOStream();
//
// count up total violations, max severity, etc.
//
GetReportNumbers();
//
// TODO: move this
//
if( SINGLE_LINE == mReportingLevel )
{
(*mpOut) << SingleLineReport() << endl;
}
else if( PARSEABLE == mReportingLevel )
{
OutputParseableReport();
}
else if( SUMMARY_ONLY <= mReportingLevel )
{
//
// output header, just like it sez....
//
OutputReportHeader();
//
// output summary info
//
OutputRulesSummary();
//
// print names of rule-breakers
//
// don't print out object summary if in CONCISE_REPORT because
// the detailed report will look a lot like the summary.
//
if( CONCISE_REPORT != mReportingLevel )
{
OutputObjectSummary();
}
if( CONCISE_REPORT <= mReportingLevel )
{
//
// print details of rule-breakers
//
OutputObjectDetails();
}
//
// output any errors
//
PrintErrors();
//
// output footer
//
OutputFooter();
}
}
void cTextReportViewer::SetUpForNewGenre( const cFCOReportGenreIter& genreIter )
{
//
// set up prop displayer and name translator
//
mpCurPD = genreIter.GetGenreHeader().GetPropDisplayer();
mpCurNT = cGenreSwitcher::GetInstance()->GetFactoryForGenre( genreIter.GetGenre() )->GetNameTranslator();
}
void cTextReportViewer::PrintErrors()
{
//
// output header
//
OutputSectionDelimiter( tw::STR_ERROR_REPORT );
//
// output any errors from the report's general error queue
//
bool fAtLeastOneGeneralError = false;
cErrorQueueIter eqIter( *( mpReport->GetErrorQueue() ) );
for( eqIter.SeekBegin(); !eqIter.Done(); eqIter.Next() )
{
ReportError( eqIter );
fAtLeastOneGeneralError = true;
}
// separate general errors from genre errors
if( fAtLeastOneGeneralError )
(*mpOut) << endl;
//
// cycle through genres
//
cFCOReportGenreIter genreIter( *mpReport );
for( genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
{
//
// set up prop displayer and name translator
//
SetUpForNewGenre( genreIter );
//
// report errors for genre
//
bool fFirstErrorInGenre = true;
cFCOReportSpecIter rsi( genreIter );
for( rsi.SeekBegin(); !rsi.Done(); rsi.Next() )
{
cErrorQueueIter eqIter2( *( rsi.GetErrorQueue() ) );
for( eqIter2.SeekBegin(); !eqIter2.Done(); eqIter2.Next() )
{
if( fFirstErrorInGenre )
{
OutputGenreDelimiter( genreIter.GetGenre() );
fFirstErrorInGenre = false;
}
ReportError( eqIter2 );
}
}
// separate genre errors
if( ! fFirstErrorInGenre )
(*mpOut) << endl;
}
//
// if there were no errors, tell user
//
if( 1 == mErrorNum )
{
(*mpOut) << TSS_GetString( cTW, tw::STR_REPORT_NO_ERRORS ) << endl << endl;
}
}
void cTextReportViewer::OutputSectionDelimiter( int nString )
{
(*mpOut) << g_sz79Equals << endl;
(*mpOut) << TSS_GetString( cTW, nString ) << endl;
(*mpOut) << g_sz79Equals << endl << endl;
}
void cTextReportViewer::OutputGenreDelimiter( cGenre::Genre g, bool fDenoteBallotSection )
{
(*mpOut) << g_sz79Dashes << endl;
// a # denotes the ballot section
if( fDenoteBallotSection )
(*mpOut) << _T("# ");
else
(*mpOut) << _T(" ");
(*mpOut) << TSS_GetString( cTW, tw::STR_SECTION );
(*mpOut) << _T(": ") << cGenreSwitcher::GetInstance()->GenreToString( g, true ) << endl;
(*mpOut) << g_sz79Dashes << endl << endl;
}
void cTextReportViewer::OutputNumDetails( int nString, int nObjects )
{
(*mpOut) << _T(" ") << g_sz40Dashes << endl;
(*mpOut) << _T(" ") << TSS_GetString( cTW, nString ) << nObjects << endl;
(*mpOut) << _T(" ") << g_sz40Dashes << endl << endl;
}
void cTextReportViewer::OutputObjectSummary()
{
//
// check if we should output summary
//
if (!WantOutputObjectSummary())
return;
//
// output summary header
//
OutputSectionDelimiter( tw::STR_OBJECT_SUMMARY );
//
// cycle through genres
//
cFCOReportGenreIter genreIter( *mpReport );
for( genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
{
//
// set up prop displayer and name translator
//
SetUpForNewGenre( genreIter );
//
// output genre delimiter
//
OutputGenreDelimiter( genreIter.GetGenre(), true );
//
// output object summary
//
OutputObjectSummary( genreIter );
}
}
void cTextReportViewer::OutputObjectSummary( const cFCOReportGenreIter& genreIter )
{
// iterate over all report elements (keyed by a spec), and save
// references to all added, removed, and changed FCOs. We do this
// because we want to group the FCOs by added, removed, and changed,
// not by spec.
//
// if we're updating, we need to save a list of names
//
FCOList* pFCONameList = NULL;
if( mfUpdate )
pFCONameList = new FCOList;
cFCOReportSpecIter ri( genreIter );
bool violationReported = false;
for( ri.SeekBegin(); !ri.Done(); ri.Next() )
{
// skip this spec if it's not one we care about for this report
if (IgnoreThisSpec(ri.GetAttr()))
continue;
//
// output a spec header if it was violated
//
if( ! SpecEmpty( ri ) )
{
violationReported = true;
OutputSpecHeader( ri );
(*mpOut) << endl;
if( mfUpdate )
{
(*mpOut) << TSS_GetString( cTW, tw::STR_ADD_X ) << endl;
(*mpOut) << endl;
}
//
// output added set
//
OutputAddedSummary( ri, pFCONameList );
//
// output removed set
//
OutputRemovedSummary( ri, pFCONameList );
//
// output changed set
//
OutputChangedSummary( ri, pFCONameList );
}
}
if (violationReported == false)
{
// There are no violations for this section
(*mpOut) << TSS_GetString( cTW, tw::STR_NO_VIOLATIONS_IN_SECTION ) << endl << endl;
}
//
// insert FCO list into master list
//
if( mfUpdate )
mFCOsRemoveFromReport.insert( cTextReportViewer::GenreList::value_type( genreIter.GetGenre(), pFCONameList ) );
}
void cTextReportViewer::OutputObjectDetails()
{
//
// check if we should output details
//
if (!WantOutputObjectDetails())
return;
//
// output detail header
//
OutputSectionDelimiter( tw::STR_OBJECT_DETAIL );
//
// cycle through genres
//
cFCOReportGenreIter genreIter( *mpReport );
for(genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
{
//
// set up prop displayer and name translator
//
SetUpForNewGenre( genreIter );
//
// output genre delimiter
//
OutputGenreDelimiter( genreIter.GetGenre() );
//
// output details
//
OutputObjectDetails( genreIter );
}
}
void cTextReportViewer::OutputObjectDetails( const cFCOReportGenreIter& genreIter )
{
//
// Now enumerate all changes
//
cFCOReportSpecIter ri( genreIter );
bool violationReported = false;
for(ri.SeekBegin() ; !ri.Done(); ri.Next() )
{
// skip this spec if it's not one we care about for this report
if (IgnoreThisSpec(ri.GetAttr()))
continue;
//
// output spec section header if there are changes
//
if( ! SpecEmpty( ri ) )
{
violationReported = true;
OutputSpecHeader( ri );
//
// output added set
//
OutputAddedDetails( ri );
//
// output removed set
//
OutputRemovedDetails( ri );
//
// output changed set
//
OutputChangedDetails( ri );
}
} // enum change details
if (violationReported == false)
{
// There are no violations for this section
(*mpOut) << TSS_GetString( cTW, tw::STR_NO_VIOLATIONS_IN_SECTION ) << endl << endl;
}
}
void cTextReportViewer::OutputFooter()
{
(*mpOut) << g_sz79Dashes << endl;
(*mpOut) << TSS_GetString( cTW, tw::STR_END_OF_REPORT ) << endl << endl;
(*mpOut) << TSS_GetString( cTW, tw::STR_COPYRIGHT ) << endl;
}
bool cTextReportViewer::LaunchEditorOnFile( const TSTRING& strFilename, const TSTRING& edName ) //throw (eTextReportViewer)
{
bool fRanViewer = false;
TSTRING editor = edName;
// make sure we can read from this file
cFileUtil::TestFileReadable( strFilename );
// editor is going to need terminal type, so tell msystem to include
// it in environment when it makes its system call.
le_set("TERM");
le_set("DISPLAY"); // DISPLAY and HOME needed so we can launch X apps. X apps apparently check
le_set("HOME"); // a .xpermissions file in the users home dir
le_set("LANG"); // LANG allowed through in case any apps need
int systemRet = msystem( (char*) ( ( editor+ _T(' ') + strFilename ).c_str() ) );
le_unset("LANG");
le_unset("HOME");
le_unset("DISPLAY");
le_unset("TERM");
if( 0 == systemRet )
{
fRanViewer = true;
}
else
{
//ASSERT( false );
throw eTextReportViewerEditorLaunch( edName );
}
return( fRanViewer );
}
void cTextReportViewer::ReportError( const cErrorQueueIter& eqIter )
{
const int nWidth = 5;
// output error number
TOSTRINGSTREAM ostr;
ostr << mErrorNum << _T(".");
(*mpOut).width( nWidth );
(*mpOut) << ostr.str();
// output general error
(*mpOut) << cErrorTable::GetInstance()->Get( eqIter.GetError().GetID() )<< endl;
// output specific error
(*mpOut).width( nWidth );
(*mpOut) << _T(""); // this is here so the next output is 10 chars over
(*mpOut) << cDisplayUtil::FormatMultiLineString(
cDisplayEncoder::EncodeInlineAllowWS(
eqIter.GetError().GetMsg() ), nWidth, 0 ) << endl;
mErrorNum++;
}
void cTextReportViewer::PrintBallotLine( const iFCO& FCO )
{
ASSERT( mpOut != 0 );
//
// output "[X] <name of FCO>"
//
if( mfUpdate ) //only have Xs for updating
{
(*mpOut) << _T("[x] "); // TODO: should this be in resources?
}
TSTRING strEncoded = mpCurNT->ToStringDisplay( FCO.GetName(), true );
(*mpOut) << strEncoded.c_str();
(*mpOut) << endl;
}
// looks for "\n[" and leaves the file pointer at the next character
// could be a problem if the ballot box is the first line in the file, then "\n["
// wouldn't be encountered, only "[", so make sure that the ballots aren't
// on the first line
// returns false if finds "\n#" -- start of new genre
int cTextReportViewer::FindNextLineToken()
{
// loop over chars until (1) EOF is hit, or (2) we find "\n[" or '\n#'
for( GetChar(); !mpIn->eof(); GetChar() )
{
if( mCurrentChar[0] == '\n' )
{
// we'll only compare to single byte chars
// so it's ok to use PeekChar() instead of our GetChar()
if( PeekChar() == '[' )
{
return TOKEN_BALLOT_BOX;
}
else if( PeekChar() == '#' ) // start of new genre
{
return TOKEN_GENRE;
}
}
}
return( TOKEN_EOF );
}
void cTextReportViewer::ReadTextReport( const TSTRING& strFilename ) //throw (eTextReportViewer)
{
std::ifstream in;
OpenInputFile( in, strFilename );
mpIn = &in;
//
// for each genre and ballot box in text report file
//
bool fMoreTokens = true;
FCOList* pCurList = NULL;
while( fMoreTokens )
{
switch( FindNextLineToken() )
{
case TOKEN_GENRE:
GetGenreInfo( &pCurList );
break;
case TOKEN_BALLOT_BOX:
GetBallotInfo( pCurList );
break;
case TOKEN_EOF:
fMoreTokens = false;
break;
default:
ASSERT( false );
break;
}
}
//
// close the file
//
in.close();
}
void cTextReportViewer::GetGenreInfo( FCOList** ppCurList )
{
//
// identify genre
//
TSTRING strGenre = GetGenre();
ASSERT( ! strGenre.empty() );
cGenre::Genre g = cGenreSwitcher::GetInstance()->StringToGenre( strGenre.c_str() );
if( cGenre::GENRE_INVALID == g)
{
throw eTextReportViewerReportCorrupt("Invalid Genre"); // TODO: ERR_UKNOWN_GENRE
}
cGenreSwitcher::GetInstance()->SelectGenre( g );
//
// get list of fcos in report for this genre
//
GenreList::iterator curIter = mFCOsRemoveFromReport.find( g );
if( curIter == mFCOsRemoveFromReport.end() )
throw eTextReportViewerReportCorrupt("No files found in report"); // TODO: ERR_UKNOWN_GENRE
*ppCurList = curIter->second;
//
// get the prop displayer
//
mpCurPD = cGenreSwitcher::GetInstance()->GetFactoryForGenre( g )->CreatePropDisplayer();
mpCurNT = cGenreSwitcher::GetInstance()->GetFactoryForGenre( g )->GetNameTranslator();
}
void cTextReportViewer::GetBallotInfo( FCOList* pCurList )
{
if( ! pCurList )
throw eTextReportViewerReportCorrupt("No ballot list found");
// if the box is checked, then the user elected to leave the item
// in the report, so we do nothing. If the box isn't checked, then
// we must remove the item from the report.
if( IsChecked() )
{
//
// look at the ballot line and get the name of the FCO
//
cFCOName fcoName;
GetFCONameFromBallotLine( fcoName );
///////////////////////////////////////
// remove this FCO from list
cTextReportViewer::FCOList::const_iterator iter;
iter = pCurList->find( fcoName );
if( iter == pCurList->end() )
{
throw eTextReportViewerReportCorrupt("Unknown file name in report");// TODO: ERR_UKNOWN_NAME
}
pCurList->erase( fcoName );
///////////////////////////////////////
}
}
// reads input up to EOL or EOF
TSTRING cTextReportViewer::GetGenre()
{
std::string strGenre;
EatSection();
while( true )
{
char chFirst = PeekChar();
if( PeekIsEOF() ||
chFirst == '\r' ||
chFirst == '\n' )
break;
// eat up char
GetChar();
AppendChar( strGenre );
}
return cStringUtil::StrToTstr( strGenre );
}
bool cTextReportViewer::PeekIsEOF()
{
return( mpIn->peek() == char_traits<char>::eof() );
}
// if the next character in the stream is ('X' or 'x'), it eats the x, else it returns false
// if the next character after the x is ']', it eats that, else it returns false.
bool cTextReportViewer::IsChecked()
{
bool fIsChecked = false;
char chFirst;
GetChar(); // eat up '['
chFirst = PeekChar(); // look for 'X'
if( ( ! PeekIsEOF() ) &&
( chFirst == 'X' || chFirst == 'x' ) ) // TODO: X in resources?
{
mpIn->get( chFirst ); // eat up X
char chSecond = PeekChar();
if( ( ! PeekIsEOF() ) &&
( chSecond == ']' ) )
{
fIsChecked = true;
mpIn->get( chSecond ); // eat up ']'
}
}
return( fIsChecked );
}
// FCO names are '\"' delimited and begin after the first '\"' after the ballot box
// NOTE: will not eat up a return character
void cTextReportViewer::GetFCONameFromBallotLine( cFCOName& fcoName ) //throw (eTextReportViewer)
{
char chIn;
std::string strFCOName;
#define TW_IS_QUOTE( ch ) ( ch == '\"' )
#define TW_IS_EOL( ch ) ( ch == '\n' || ch == '\r' )
#define TW_IS_BACKSLASH( ch ) ( ch == '\\' )
//
// go past any character that's not '\"' or EOL
// if we hit EOL, then there was no fconame present, so error
//
for( chIn = PeekChar();
! PeekIsEOF();
chIn = PeekChar() ) // while not EOF
{
if( TW_IS_QUOTE( chIn ) )
{
mpIn->get( chIn ); // eat up all chars but EOL
strFCOName += chIn; // add quote to string
// we've found our quote, break and get name
break;
}
else if( TW_IS_EOL( chIn ) )
{
// if EOL, there was no name!
throw eTextReportViewerReportCorrupt("Ballot item without a name");
}
else
{
GetChar(); // eat up all chars but EOL
}
}
//
// get FCO name (chars up until the next '<EOL>')
//
for( chIn = PeekChar();
! PeekIsEOF();
chIn = PeekChar() ) // while not EOF
{
if( TW_IS_EOL( chIn ) ) // if EOL, there was no ending quote!
{
// found end of fconame
break;
}
else
{
// add char to fco name
GetChar();
AppendChar( strFCOName );
}
}
// make sure we've got a name
if( strFCOName.empty()
||
! mpCurNT->DisplayStringToFCOName(
cStringUtil::StrToTstr( strFCOName ),
fcoName ) )
{
std::string msg = "Invalid object name: " + strFCOName;
throw eTextReportViewerReportCorrupt(msg); // TODO -- it might be nice to be able to specify what line of the report got corrupted
}
}
void cTextReportViewer::RemoveFCOsFromReport() //throw (eTextReportViewer)
{
// TODO: remove fconames from fcosToRemove when we find them in report,
// so we don't compare that fconame again. Watch out for messing
// with iters in the for, loops, though! That's why I didn't do
// it yet... (and I figure most users would only un-X a few items,
// in which case it is currently faster anyways...)
//
// iterate over all specs in report, then in each of the added,
// removed, and changed sets, lookup the FCOName and, if found,
// delete it
//
int nFCOsRemoved = 0;
cFCOReportGenreIter rgi( *mpReport );
for( rgi.SeekBegin(); !rgi.Done(); rgi.Next() )
{
cTextReportViewer::GenreList::iterator currentList = mFCOsRemoveFromReport.find( rgi.GetGenre() );
ASSERT( currentList != mFCOsRemoveFromReport.end() );
cTextReportViewer::FCOList* pFCOList = currentList->second;
cFCOReportSpecIter rsi( rgi );
for( rsi.SeekBegin(); !rsi.Done(); rsi.Next() )
{
// search added
const iFCOSet* pAddedSet = rsi.GetAddedSet();
ASSERT( pAddedSet != 0 );
const iFCOIter* fcoIter = NULL;
cTextReportViewer::FCOList::iterator iter;
//
// see if any FCOs to remove are in this set
//
for( iter = pFCOList->begin(); iter != pFCOList->end(); ++iter )
{
fcoIter = pAddedSet->Lookup( *iter );
if( fcoIter )
{
fcoIter->Remove();
nFCOsRemoved++;
fcoIter->DestroyIter();
}
}
// search removed
// get removed set
const iFCOSet* pRemovedSet = rsi.GetRemovedSet();
ASSERT( pRemovedSet != 0 );
//
// see if any FCOs to remove are in this set
//
for( iter = pFCOList->begin(); iter != pFCOList->end(); ++iter )
{
fcoIter = pRemovedSet->Lookup( *iter );
if( fcoIter )
{
fcoIter->Remove();
nFCOsRemoved++;
fcoIter->DestroyIter();
}
}
for( iter = pFCOList->begin(); iter != pFCOList->end(); ++iter )
{
// search changed
// get changed set iterator
cFCOReportChangeIter changedIter( rsi );
// iterate over all changed fcos
for( changedIter.SeekBegin(); !changedIter.Done(); changedIter.Next() )
{
cFCOName name = changedIter.GetOld()->GetName();
if( name.IsEqual( *iter ) )
{
changedIter.Remove();
nFCOsRemoved++;
}
}
}
}
}
//
// count FCOs to remove
//
int nFCOsToRemove = 0;
cTextReportViewer::GenreList::const_iterator iter = mFCOsRemoveFromReport.begin();
for( ; iter != mFCOsRemoveFromReport.end(); ++iter )
nFCOsToRemove += iter->second->size();
if( nFCOsToRemove != nFCOsRemoved )
{
// TODO -- maybe have a different enumeration for this?
throw eTextReportViewerReportCorrupt("Mismatch in objects to remove");
}
}
void cTextReportViewer::OutputReportHeader()
{
if (!WantOutputReportHeader())
return;
const int headerColumnWidth = 30;
(*mpOut) << TSS_GetString( cTW, tw::STR_REPORT_TITLE ) << endl << endl;
(*mpOut) << setw(headerColumnWidth)
<< TSS_GetString( cTW, tw::STR_R_GENERATED_BY )
<< util_Encode( mpHeader->GetCreator() )
<< endl;
// TODO: ( start / end / elapsed ) time
TSTRING tstrDummy;
int64 i64CreateTime = mpHeader->GetCreationTime();
(*mpOut).width(headerColumnWidth);
(*mpOut) << TSS_GetString( cTW, tw::STR_R_CREATED_ON ) << cTWLocale::FormatTime( i64CreateTime, tstrDummy ).c_str() << endl;
(*mpOut).width(headerColumnWidth);
(*mpOut) << TSS_GetString( cTW, tw::STR_DB_LAST_UPDATE );
int64 i64LastDBUTime = mpHeader->GetLastDBUpdateTime();
if( i64LastDBUTime == 0 )
{
(*mpOut) << TSS_GetString( cTW, tw::STR_NEVER ) << endl << endl;
}
else
{
(*mpOut) << cTWLocale::FormatTime( i64LastDBUTime, tstrDummy ).c_str() << endl << endl;
}
OutputSectionDelimiter( tw::STR_R_SUMMARY );
(*mpOut).width(headerColumnWidth);
(*mpOut) << TSS_GetString( cTW, tw::STR_HOST_NAME ) << mpHeader->GetSystemName().c_str() << endl;
(*mpOut).width(headerColumnWidth);
(*mpOut) << TSS_GetString( cTW, tw::STR_HOST_IP ) << mpHeader->GetIPAddress() << endl;
(*mpOut).width(headerColumnWidth);
(*mpOut) << TSS_GetString( cTW, tw::STR_HOST_ID );
if(! mpHeader->GetHostID().empty() )
(*mpOut) << mpHeader->GetHostID() << endl;
else
(*mpOut) << TSS_GetString( cTW, tw::STR_NONE ) << endl;
(*mpOut) << setw(headerColumnWidth)
<< TSS_GetString( cTW, tw::STR_POLICY_FILE_USED )
<< util_Encode( mpHeader->GetPolicyFilename() )
<< endl;
(*mpOut) << setw(headerColumnWidth)
<< TSS_GetString( cTW, tw::STR_CONFIG_FILE_USED )
<< util_Encode( mpHeader->GetConfigFilename() )
<< endl;
(*mpOut) << setw(headerColumnWidth)
<< TSS_GetString( cTW, tw::STR_DB_FILE_USED )
<< util_Encode( mpHeader->GetDBFilename() )
<< endl;
(*mpOut) << setw(headerColumnWidth)
<< TSS_GetString( cTW, tw::STR_CMD_LINE_USED )
<< util_Encode( mpHeader->GetCommandLineParams() )
<< endl
<< endl;
}
void cTextReportViewer::OutputRulesSummary()
{
//
// check if we should output summary
//
if( !WantOutputRulesSummary() )
return;
//
// output summary header
//
OutputSectionDelimiter( tw::STR_RULE_SUMMARY );
//
// cycle through genres
//
cFCOReportGenreIter genreIter( *mpReport );
for( genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
{
//
// set up prop displayer and name translator
//
SetUpForNewGenre( genreIter );
//
// output genre delimiter
//
OutputGenreDelimiter( genreIter.GetGenre() );
//
// output rules summary
//
OutputRulesSummary( genreIter );
}
}
void cTextReportViewer::OutputRulesSummary( const cFCOReportGenreIter& genreIter )
{
// output spec stats header
// 2 + specNameWidth + severityWidth + 3*numObjectsWidth should be < 80
const int specNameWidth = 32;
const int severityWidth = 18;
const int numObjectsWidth = 9;
(*mpOut) << _T(" "); // indent
(*mpOut).width( specNameWidth );
(*mpOut) << TSS_GetString( cTW, tw::STR_RULE_NAME );
(*mpOut).width( severityWidth );
(*mpOut) << TSS_GetString( cTW, tw::STR_SEVERITY_LEVEL );
(*mpOut).width( numObjectsWidth );
(*mpOut) << TSS_GetString( cTW, tw::STR_ADDED );
(*mpOut).width( numObjectsWidth );
(*mpOut) << TSS_GetString( cTW, tw::STR_REMOVED );
(*mpOut).width( numObjectsWidth );
(*mpOut) << TSS_GetString( cTW, tw::STR_CHANGED );
(*mpOut) << endl;
(*mpOut) << _T(" ");
(*mpOut) << setw( specNameWidth ) << _T("---------");
(*mpOut) << setw( severityWidth ) << _T("--------------");
(*mpOut) << setw( numObjectsWidth ) << _T("-----");
(*mpOut) << setw( numObjectsWidth ) << _T("-------");
(*mpOut) << setw( numObjectsWidth ) << _T("--------");
(*mpOut) << endl;
// merge rules of duplicate names and severities
ObjectSummaryList summaryList;
CollateRulesSummary( genreIter, summaryList );
// Output the information in the list
int nTotalObjectsChanged = 0;
ObjectSummaryList::iterator si;
for (si = summaryList.begin(); si != summaryList.end(); ++si)
{
// highlight violated rules
if( si->mAddedObjects + si->mRemovedObjects + si->mChangedObjects > 0 )
(*mpOut) << _T("* ");
else
(*mpOut) << _T(" "); // indent
TSTRING strSpecName = util_Encode( si->mSpecName );
(*mpOut) << setw( specNameWidth )
<< strSpecName;
// if the name is too long, put it on its own line
if( strSpecName.length() >= (unsigned int)specNameWidth )
{
(*mpOut) << endl;
(*mpOut) << _T(" "); // indent
(*mpOut).width( specNameWidth );
(*mpOut) << _T(""); // output space holder
}
(*mpOut).width( severityWidth );
(*mpOut) << si->mSeverity;
(*mpOut).width( numObjectsWidth );
(*mpOut) << si->mAddedObjects;
(*mpOut).width( numObjectsWidth );
(*mpOut) << si->mRemovedObjects;
(*mpOut).width( numObjectsWidth );
(*mpOut) << si->mChangedObjects;
(*mpOut) << endl;
// output start point on next line
// TODO: I am not sure if I like this in the new collated reports, dmb Sept 16 1999
if (!si->mStartPoint.empty())
(*mpOut) << _T(" (") << si->mStartPoint << _T(")") << endl;
nTotalObjectsChanged += si->mAddedObjects + si->mRemovedObjects + si->mChangedObjects;
}
(*mpOut) << endl;
(*mpOut) << TSS_GetString( cTW, tw::STR_OBJECTS_SCANNED ) << _T(" ") << genreIter.GetGenreHeader().GetObjectsScanned() << endl;
(*mpOut) << TSS_GetString( cTW, tw::STR_TOTAL_VIOLATIONS ) << _T(" ") << nTotalObjectsChanged << endl;
(*mpOut) << endl;
}
void cTextReportViewer::CollateRulesSummary( const cFCOReportGenreIter& genreIter, ObjectSummaryList& summaryList )
{
summaryList.clear();
cFCOReportSpecIter ri( genreIter );
for( ri.SeekBegin(); !ri.Done(); ri.Next() )
{
// skip this spec if it's not one we care about for this report
if (IgnoreThisSpec(ri.GetAttr()))
continue;
RuleSummaryLine newLine;
newLine.mSpecName = util_Encode( ri.GetSpec()->GetName() );
newLine.mSeverity = ri.GetAttr()->GetSeverity();
newLine.mAddedObjects = ri.GetAddedSet()->Size();
newLine.mRemovedObjects = ri.GetRemovedSet()->Size();
newLine.mChangedObjects = ri.GetNumChanged();
newLine.mStartPoint = mpCurNT->ToStringDisplay( ri.GetSpec()->GetStartPoint() );
// If there is already a summary line for this rule with equivalent severity, merge this one into it
ObjectSummaryList::iterator si;
for (si = summaryList.begin(); ; ++si)
{
if (si == summaryList.end())
{
summaryList.push_back(newLine);
break;
}
if (si->mSpecName.compare(newLine.mSpecName) == 0 &&
si->mSeverity == newLine.mSeverity)
{
si->mAddedObjects += newLine.mAddedObjects;
si->mRemovedObjects += newLine.mRemovedObjects;
si->mChangedObjects += newLine.mChangedObjects;
// if one of the start points is a subset of the other, then we take the shorter one.
// otherwise we set the startpoint to empty.
if (newLine.mStartPoint.length() <= si->mStartPoint.length())
if (newLine.mStartPoint.compare(0, newLine.mStartPoint.length(), si->mStartPoint) == 0)
si->mStartPoint = newLine.mStartPoint;
else
si->mStartPoint.erase();
else
if (si->mStartPoint.compare(0, si->mStartPoint.length(), newLine.mStartPoint) == 0)
;
else
si->mStartPoint.erase();
break;
}
}
}
}
void cTextReportViewer::OutputSpecHeader( const cFCOReportSpecIter &ri )
{
if (!WantOutputSpecHeader())
return;
(*mpOut) << g_sz79Dashes << endl;
(*mpOut) << TSS_GetString( cTW, tw::STR_RULE_NAME ) << _T(": ") << util_Encode( ri.GetSpec()->GetName() );
(*mpOut) << _T( " (" ) << mpCurNT->ToStringDisplay( ri.GetSpec()->GetStartPoint() ) << _T( ")" ) << endl;
(*mpOut) << TSS_GetString( cTW, tw::STR_SEVERITY_LEVEL ) << _T(": ") << ri.GetAttr()->GetSeverity() << endl;
(*mpOut) << g_sz79Dashes << endl;
}
void cTextReportViewer::DisplayChangedProps( const iFCO* const pfcoOld, const iFCO* const pfcoNew, const cFCOPropVector* pv )
{
ASSERT( mpOut != 0 );
ASSERT( pfcoOld || pfcoNew); // have to have at least one to display
const iFCO* pfcoValid = pfcoOld ? pfcoOld : pfcoNew; // we need to use one of the pfco's for some Get functions
// marginWidth + attrNameWidth + ( 2 * attrValueWidth ) should be < 80
const int attrNameWidth = 21;
const int attrValueWidth = 28;
const int marginWidth = 2; // for " " or "* "
ASSERT( ( attrNameWidth + ( 2 * attrValueWidth ) + marginWidth ) < 80 );
// output header
(*mpOut) << setw( marginWidth ) << _T("");
(*mpOut) << setw( attrNameWidth ) << TSS_GetString( cTW, tw::STR_ATTRIBUTES );
(*mpOut) << setw( attrValueWidth ) << TSS_GetString( cTW, tw::STR_EXPECTED );
(*mpOut) << setw( attrValueWidth ) << TSS_GetString( cTW, tw::STR_OBSERVED );
(*mpOut) << endl;
(*mpOut) << setw( marginWidth ) << _T("");
(*mpOut) << setw( attrNameWidth ) << _T("-------------");
(*mpOut) << setw( attrValueWidth ) << _T("-----------");
(*mpOut) << setw( attrValueWidth ) << _T("-----------");
(*mpOut) << endl;
int iNumProps = pfcoValid->GetPropSet()->GetNumProps(); // TODO: what if new + old have different props
for( int j = 0; j < iNumProps; j++ )
{
// if not a full report, only print properties that have changed
if( FULL_REPORT != mReportingLevel )
{
if( !pv || !pv->ContainsItem( j ) )
continue;
}
// ignore certain combinations of undef and non-existent props
if( ! PrintableProp( pfcoOld, pfcoNew, j ) )
continue;
// highlight changes and set width for prop name
if( ! mpCurPD->IsMultiLineProp( j ) )
{
if( !pv || pv->ContainsItem( j ) )
(*mpOut) << setw( marginWidth ) << _T("*");
else
(*mpOut) << setw( marginWidth ) << _T("");
}
else // for multiline props we will highlight the observed prop if it has changed
{
(*mpOut) << setw( marginWidth ) << _T("");
}
//
// output prop name
//
{
TSTRING strNewName;
strNewName = pfcoValid->GetPropSet()->GetPropName(j);
if( mpCurPD->IsMultiLineProp( j ) )
{
strNewName += _T(" ") ;
strNewName += TSS_GetString( cTW, tw::STR_EXPECTED );
}
//strNewName += _T(':') ;
(*mpOut) << setw( attrNameWidth ) << strNewName;
}
//
// output old value
//
TSTRING strOldValue;
(*mpOut).width( attrValueWidth );
if( pfcoOld && pfcoOld->GetPropSet()->GetValidVector().ContainsItem( j ) )
strOldValue = mpCurPD->PropAsString( pfcoOld, j, ( marginWidth + attrNameWidth ), 0 ).c_str();
else
strOldValue = _T("---");
(*mpOut) << strOldValue;
//
// output name again, if necessary (if multiline prop)
//
if( mpCurPD->IsMultiLineProp( j ) )
{
TSTRING strNewName;
strNewName = pfcoValid->GetPropSet()->GetPropName(j);
strNewName += _T(" ") ;
strNewName += TSS_GetString( cTW, tw::STR_OBSERVED );
//strNewName += _T(':') ;
(*mpOut) << endl;
if( !pv || pv->ContainsItem( j ) )
(*mpOut) << setw( marginWidth ) << _T("*");
else
(*mpOut) << setw( marginWidth ) << _T("");
(*mpOut) << setw( attrNameWidth ) << strNewName;
}
else
{
// if old value would run into this value, we need to put this value
// on the next line (but in the same column). don't output
// value but position "cursor" in the correct place
if( strOldValue.length() >= (unsigned int)attrValueWidth )
{
(*mpOut) << endl;
(*mpOut) << setw( marginWidth ) << _T("");
(*mpOut) << setw( attrNameWidth ) << _T("");
(*mpOut) << setw( attrValueWidth ) << _T("");
}
}
//
// output new value
//
(*mpOut).width( attrValueWidth );
if( pfcoNew && pfcoNew->GetPropSet()->GetValidVector().ContainsItem( j ) )
(*mpOut) << mpCurPD->PropAsString( pfcoNew, j, ( marginWidth + attrNameWidth ), 0 ).c_str();
else
(*mpOut) << _T("---");
(*mpOut) << endl;
}
}
void cTextReportViewer::OutputAddedSummary( const cFCOReportSpecIter& ri, FCOList* pFCONameList )
{
if( ! ri.GetAddedSet()->IsEmpty() )
{
(*mpOut) << TSS_GetString( cTW, tw::STR_ADDED ) << _T(":") << endl;
// iterate over all fcos
const cIterProxy<iFCOIter> pSetIterAdded = ri.GetAddedSet()->GetIter();
for( pSetIterAdded->SeekBegin(); !pSetIterAdded->Done(); pSetIterAdded->Next() )
{
PrintBallotLine( *pSetIterAdded->FCO() );
// if we're updating, save a list of FCO names
if( mfUpdate && pFCONameList)
pFCONameList->insert( pSetIterAdded->FCO()->GetName() );
}
(*mpOut) << endl;
}
}
void cTextReportViewer::OutputRemovedSummary( const cFCOReportSpecIter& ri, FCOList* pFCONameList)
{
if( ! ri.GetRemovedSet()->IsEmpty() )
{
(*mpOut) << TSS_GetString( cTW, tw::STR_REMOVED ) << _T(":") << endl;
// iterate over all fcos
const cIterProxy<iFCOIter> pSetIterRemoved = ri.GetRemovedSet()->GetIter();
for( pSetIterRemoved->SeekBegin(); !pSetIterRemoved->Done(); pSetIterRemoved->Next() )
{
PrintBallotLine( *pSetIterRemoved->FCO() );
// if we're updating, save a list of FCO names
if( mfUpdate && pFCONameList)
pFCONameList->insert( pSetIterRemoved->FCO()->GetName() );
}
(*mpOut) << endl;
}
}
void cTextReportViewer::OutputChangedSummary( const cFCOReportSpecIter& ri, FCOList* pFCONameList )
{
if( ri.GetNumChanged() > 0 )
{
(*mpOut) << TSS_GetString( cTW, tw::STR_CHANGED ) << _T(":") << endl;
// iterate over all changed fcos
cFCOReportChangeIter changedIter( ri );
for( changedIter.SeekBegin(); !changedIter.Done(); changedIter.Next() )
{
PrintBallotLine( *changedIter.GetNew() );
// if we're updating, save a list of FCO names
if( mfUpdate && pFCONameList)
pFCONameList->insert( changedIter.GetNew()->GetName() );
}
(*mpOut) << endl;
}
}
void cTextReportViewer::OutputAddedDetails( const cFCOReportSpecIter& ri )
{
if( ! ri.GetAddedSet()->IsEmpty() )
{
OutputNumDetails( tw::STR_ADDED_FILES, ri.GetAddedSet()->Size() );
// iterate over all fcos
const cIterProxy<iFCOIter> pSetIterAdded = ri.GetAddedSet()->GetIter(); ASSERT( pSetIterAdded != 0 );
for( pSetIterAdded->SeekBegin(); !pSetIterAdded->Done(); pSetIterAdded->Next() )
{
(*mpOut) << TSS_GetString( cTW, tw::STR_ADDED_FILE_NAME ) << _T(" ") << mpCurNT->ToStringDisplay( pSetIterAdded->FCO()->GetName() ).c_str() << endl;
if( FULL_REPORT == mReportingLevel )
{
(*mpOut) << endl;
DisplayChangedProps( NULL, pSetIterAdded->FCO(), NULL );
(*mpOut) << endl << endl;
}
}
(*mpOut) << endl;
}
}
void cTextReportViewer::OutputRemovedDetails( const cFCOReportSpecIter& ri )
{
if( ! ri.GetRemovedSet()->IsEmpty() )
{
OutputNumDetails( tw::STR_REMOVED_FILES, ri.GetRemovedSet()->Size() );
// iterate over all fcos
const cIterProxy<iFCOIter> pSetIterRemoved = ri.GetRemovedSet()->GetIter(); ASSERT( pSetIterRemoved != 0 );
for( pSetIterRemoved->SeekBegin(); !pSetIterRemoved->Done(); pSetIterRemoved->Next() )
{
(*mpOut) << TSS_GetString( cTW, tw::STR_REMOVED_FILE_NAME ) << _T(" ") << mpCurNT->ToStringDisplay( pSetIterRemoved->FCO()->GetName() ).c_str() << endl;
if( FULL_REPORT == mReportingLevel )
{
(*mpOut) << endl;
DisplayChangedProps( pSetIterRemoved->FCO(), NULL, NULL );
(*mpOut) << endl << endl;
}
}
(*mpOut) << endl;
}
}
void cTextReportViewer::OutputChangedDetails( const cFCOReportSpecIter& ri )
{
if( ri.GetNumChanged() > 0 )
{
OutputNumDetails( tw::STR_CHANGED_FILES, ri.GetNumChanged() );
// iterate over all changed fcos
cFCOReportChangeIter changedIter( ri );
for( changedIter.SeekBegin(); !changedIter.Done(); changedIter.Next() )
{
(*mpOut) << TSS_GetString( cTW, tw::STR_CHANGED_FILE_NAME ) << _T(" ") << mpCurNT->ToStringDisplay( changedIter.GetOld()->GetName() ).c_str() << endl << endl;
DisplayChangedProps( changedIter.GetOld(), changedIter.GetNew(), &changedIter.GetChangeVector() );
(*mpOut) << endl << endl;
}
(*mpOut) << endl;
}
}
// overridables:
// These function allows derived classes to tailor reports
bool cTextReportViewer::IgnoreThisSpec(const cFCOSpecAttr *attr) { return false; }
bool cTextReportViewer::WantOutputReportHeader() { return true; }
bool cTextReportViewer::WantOutputRulesSummary() { return true; }
bool cTextReportViewer::WantOutputSpecHeader() { return true; }
bool cTextReportViewer::WantOutputObjectSummary() { return true; }
bool cTextReportViewer::WantOutputObjectDetails() { return true; }
bool cTextReportViewer::CanUpdate() { return true; }
//=========================================================================
// UTIL FUNCTION CODE
//=========================================================================
void OpenInputFile( std::ifstream& in, const TSTRING& strFile ) // throw( eTextReportViewer )
{
in.open( cStringUtil::TstrToStr( strFile ).c_str() );
if(! in.is_open())
{
throw eTextReportViewerFileOpen( strFile );
}
}
void OpenOutputFile( fixed_basic_ofstream<TCHAR>& out, const TSTRING& strFile ) // throw( eTextReportViewer )
{
std::string narrowFilename = cStringUtil::TstrToStr( strFile );
// we explicitly create the file so that we control the
// permissions on the new file.
unlink(narrowFilename.c_str());
int fd = open(narrowFilename.c_str(), O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 00600);
if (fd == -1 || close(fd) != 0) {
throw eTextReportViewerFileOpen( strFile );
}
out.open( narrowFilename.c_str() );
if(! out.is_open())
throw eTextReportViewerFileOpen( strFile );
}
bool SpecEmpty( const cFCOReportSpecIter& ri )
{
return( ri.GetAddedSet()->IsEmpty() && ri.GetRemovedSet()->IsEmpty() && 0 == ri.GetNumChanged() );
}
bool PrintableProp( const iFCO* pfcoOld, const iFCO* pfcoNew, int j )
{
bool fOldExists = pfcoOld && pfcoOld->GetPropSet()->GetValidVector().ContainsItem( j );
bool fNewExists = pfcoNew && pfcoNew->GetPropSet()->GetValidVector().ContainsItem( j );
//
// don't output line if neither FCO has the prop
//
if( ! fOldExists && ! fNewExists )
{
return false;
}
//
// don't output anything if both props are undefined
//
if ( fOldExists && fNewExists &&
pfcoOld->GetPropSet()->GetPropAt( j )->GetType()
== cFCOUndefinedProp::GetInstance()->GetType() &&
pfcoNew->GetPropSet()->GetPropAt( j )->GetType()
== cFCOUndefinedProp::GetInstance()->GetType() )
{
return false;
}
//
// don't output anything if old is undef and new doesn't exist
//
if(
fOldExists
&&
! fNewExists
&&
pfcoOld->GetPropSet()->GetPropAt( j )->GetType() == cFCOUndefinedProp::GetInstance()->GetType()
)
{
return false;
}
//
// don't output anything if new is undef and old doesn't exist
//
if(
! fOldExists
&&
fNewExists
&&
pfcoNew->GetPropSet()->GetPropAt( j )->GetType() == cFCOUndefinedProp::GetInstance()->GetType()
)
{
return false;
}
return true;
}
void cTextReportViewer::EatSection()
{
GetChar(); // get '#'
// eat up " section: "
int nb =
cStringUtil::TstrToStr(
TSS_GetString( cTW, tw::STR_SECTION )
).length();
nb += 3; // one for each space surrounding section, and one for colon
// eat up nb bytes
for( int i = 0; !mpIn->eof() && i < nb; i++ )
mpIn->get();
}
//=========================================================================
//
// Methods of class cEmailReportViewer
//
//=========================================================================
cEmailReportViewer::cEmailReportViewer( const cFCOReportHeader& h, const cFCOReport& r,
const TSTRING &address, bool bForceFullReport /*= false */)
: cTextReportViewer( h, r ), mbForceFullReport(bForceFullReport)
{
mAddress = address;
}
bool cEmailReportViewer::IgnoreThisSpec(const cFCOSpecAttr *attr)
{
// IgnoreThisSpec - returns false if the email address is specified
// for the given spec attributes.
if (mbForceFullReport)
return false;
// loop through all email addresses for this spec
cFCOSpecAttrEmailIter emailIter(*attr);
for(emailIter.SeekBegin(); ! emailIter.Done(); emailIter.Next())
{
if (emailIter.EmailAddress() == mAddress)
return false; // Don't ignore it. It applies to me.
}
return true; // Ignore it. It doesn't have my name on it.
}
bool cEmailReportViewer::WantOutputObjectSummary()
{
// This area is not useful unless you're doing the ballot box thing,
// which is not applicable to emailed reports.
return false;
}
bool cEmailReportViewer::CanUpdate()
{
// this derived class does not allow "DisplayReportAndHaveUserUpdateIt()"
return false;
}
//=========================================================================
// 6/16/99 -- new reporting stuff
//=========================================================================
TSTRING cTextReportViewer::SingleLineReport()
{
TOSTRINGSTREAM sstrReport;
ASSERT( mpReport != 0 );
ASSERT( mpHeader != 0 );
//
// get current time
//
tm* ptm = cTimeUtil::TimeToDateLocal( mpHeader->GetCreationTime() );
if( NULL == ptm )
ThrowAndAssert( ePoly() );
//
// format time: YYYYMMDDHHMMSS
//
TCHAR szDate[256];
_tcsftime( szDate, countof( szDate ), _T("%Y%m%d%H%M%S"), ptm );
sstrReport << TSS_GetString( cTW, tw::STR_TRIPWIRE_REPORT_SHORT );
sstrReport << _T(" ");
sstrReport << mpHeader->GetSystemName();
sstrReport << _T(" ");
sstrReport << szDate;
//
// get report stats
//
int nAddedTotal = 0;
int nRemovedTotal = 0;
int nChangedTotal = 0;
int nViolations = 0;
int maxSeverityViolated = 0;
cFCOReportGenreIter genreIter( *mpReport );
for( genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
{
// We need to at least get the max severity in this function
cFCOReportSpecIter ri( genreIter );
for( ri.SeekBegin(); !ri.Done(); ri.Next() )
{
int nAdded = ri.GetAddedSet()->Size();
int nRemoved = ri.GetRemovedSet()->Size();
int nChanged = ri.GetNumChanged();
nAddedTotal += nAdded;
nRemovedTotal += nRemoved;
nChangedTotal += nChanged;
if( nAdded || nRemoved || nChanged )
{
// update the max severity
if( maxSeverityViolated < ri.GetAttr()->GetSeverity() )
maxSeverityViolated = ri.GetAttr()->GetSeverity();
nViolations += ( nAdded + nRemoved + nChanged );
}
}
}
sstrReport << _T(" ") << TSS_GetString( cTW, tw::STR_VIOLATIONS_SHORT ) << _T(":") << nViolations;
sstrReport << _T(" ") << TSS_GetString( cTW, tw::STR_MAX_SEV_SHORT ) << _T(":") << maxSeverityViolated;
sstrReport << _T(" ") << TSS_GetString( cTW, tw::STR_ADDED_SHORT ) << _T(":") << nAddedTotal;
sstrReport << _T(" ") << TSS_GetString( cTW, tw::STR_REMOVED_SHORT ) << _T(":") << nRemovedTotal;
sstrReport << _T(" ") << TSS_GetString( cTW, tw::STR_CHANGED_SHORT ) << _T(":") << nChangedTotal;
return sstrReport.str();
}
void cTextReportViewer::OutputParseableReport()
{
// iterate over all genres in a report
cFCOReportGenreIter genreIter( *mpReport );
for( genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
{
SetUpForNewGenre( genreIter );
// iterate over all specs in a genre
cFCOReportSpecIter ri( genreIter );
for( ri.SeekBegin(); !ri.Done(); ri.Next() )
{
// iterate over all removed fcos
const cIterProxy<iFCOIter> pSetIterAdded = ri.GetAddedSet()->GetIter();
for( pSetIterAdded->SeekBegin(); !pSetIterAdded->Done(); pSetIterAdded->Next() )
{
(*mpOut) << TSS_GetString( cTW, tw::STR_ADDED ) << _T(":\t");
(*mpOut) << mpCurNT->ToStringDisplay( pSetIterAdded->FCO()->GetName(), true ) << endl;
}
// iterate over all removed fcos
const cIterProxy<iFCOIter> pSetIterRemoved = ri.GetRemovedSet()->GetIter();
for( pSetIterRemoved->SeekBegin(); !pSetIterRemoved->Done(); pSetIterRemoved->Next() )
{
(*mpOut) << TSS_GetString( cTW, tw::STR_REMOVED ) << _T(":\t");
(*mpOut) << mpCurNT->ToStringDisplay( pSetIterRemoved->FCO()->GetName(), true ) << endl;
}
// iterate over all changed fcos
cFCOReportChangeIter changedIter( ri );
for( changedIter.SeekBegin(); !changedIter.Done(); changedIter.Next() )
{
(*mpOut) << TSS_GetString( cTW, tw::STR_CHANGED ) << _T(":\t");
(*mpOut) << mpCurNT->ToStringDisplay( changedIter.GetNew()->GetName(), true ) << endl;
}
}
}
}
char cTextReportViewer::PeekChar()
{
return char_traits<char>::
to_char_type(
mpIn->peek() );
}
// TODO:BAM -- man is this function sloppy!!!
// The only reason that this function would fail would be that the text report
// was corrupted, like um, binarily corrupted. (that is, anything a text editor
// would put in here would be readable)
void cTextReportViewer::GetChar()
{
cDebug d("cTextReportViewer::GetChar");
ASSERT( sizeof( mCurrentChar ) >= MB_CUR_MAX ); // mCurrentChar holds a mb character for current locale
// initialize mCurrentChar
mCurrentCharSize = 0;
for( uint32 i = 0; i < sizeof( mCurrentChar ); i++ )
mCurrentChar[i] = 0;
static const std::istream::char_type eof =
std::char_traits< char >::to_char_type(std::char_traits< char >::eof() );
std::streampos pos = mpIn->tellg();
for( size_t nch = 0; nch < (size_t)MB_CUR_MAX; nch++ )
{
if( mpIn->eof() || PeekIsEOF() )
{
// should be first byte we read
if( nch != 0 )
throw eTextReportViewerReportCorrupt("Expected EOF");
if( PeekIsEOF() )
{
// get the eof char so that mpIn->eof() will return true
mpIn->get();
}
d.TraceDebug( _T("Found EOF\n") );
mCurrentChar[0] = eof;
mCurrentCharSize = 1;
return;
}
else
{
if( ! mpIn->good() )
{
d.TraceDebug( _T("Input stream error.\n") );
throw eTextReportViewerReportCorrupt("Input stream error");
}
// get character from input stream
std::istream::char_type ch = std::char_traits<char>::to_char_type(mpIn->get());
// add character to mb buffer
mCurrentChar[nch] = ch;
mCurrentCharSize++;
// have we found a complete mb character yet?
if( 0 <= mbtowc( NULL, mCurrentChar, mCurrentCharSize ) )
{
// completed a valid mb charactter
return;
}
else
{
// incomplete mb char -- keep reading in bytes
}
}
}
mpIn->seekg( pos );
std::istream::char_type c = std::char_traits<char>::to_char_type( mpIn->get() );
if( (unsigned char)c > 0x7f )
{
mCurrentChar[0] = c;
mCurrentChar[1] = 0;
mCurrentCharSize = 1;
return;
}
// sequence was not a valid mb character
// (searched MB_CUR_MAX chars and didn't find a complete mb character)
d.TraceDebug( _T("Invalid mb char found!\n") );
#ifdef _DEBUG
for( int j = 0; j < MB_CUR_MAX; j++ )
d.TraceDebug( _T("%u\n"), (size_t)(unsigned char)mCurrentChar[j] );
#endif
ASSERT( false );
throw eTextReportViewerReportCorrupt("Invalid multibyte sequence");
}
void cTextReportViewer::AppendChar( std::string& str )
{
for( size_t s = 0; s < mCurrentCharSize; s++ )
str += mCurrentChar[s];
}