// // 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 #include #include #include #ifdef HAVE_WCHAR_H # include #endif #ifdef HAVE_UNISTD_H # include #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& 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(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 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] " // 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 = ∈ // // 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::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 '') // 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 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 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 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 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& 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 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 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:: 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::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::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]; }