/*
* Copyright (C) 2007-2014 Christian Bockermann <chris@jwall.org>
*
* This file is part of the web-audit library.
*
* web-audit library is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The web-audit library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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, see <http://www.gnu.org/licenses/>.
*
*/
package org.jwall.audit.server;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import org.jwall.web.audit.AuditEvent;
import org.jwall.web.audit.AuditEventListener;
import org.jwall.web.audit.AuditEventType;
import org.jwall.web.audit.ModSecurity;
import org.jwall.web.audit.io.AbstractAuditEventReader;
import org.jwall.web.audit.io.AuditEventIterator;
import org.jwall.web.audit.io.AuditEventReader;
import org.jwall.web.audit.io.ModSecurity2AuditReader;
import org.jwall.web.audit.io.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* This class implements a parser for audit-logfile for the <code>modsecurity2</code>. It
* also implements the <code>AuditEventSource</code> interface.
*
* @author Christian Bockermann <chris@jwall.org>
*
*/
public class ModSecurity2AuditStream extends AbstractAuditEventReader
implements AuditEventReader, Runnable
{
/** A unique logger for this class */
static Logger log = LoggerFactory.getLogger( ModSecurity2AuditReader.class );
/** A flag indicating whether this reader should poll new events after hitting EOF */
boolean tail;
String prefix = null;
String sensor = "";
AuditEventListener listener;
/**
* This constructor creates an audit-event-Reader that reads from the
* given input stream.
*
* @param in The stream to read from.
* @throws IOException In case the stream cannot be opened or another IO error occurs.
*/
public ModSecurity2AuditStream( InputStream in, AuditEventListener listener )
throws IOException
{
this( in, listener, true );
}
/**
* This constructor creates an audit-event-Reader that reads from the
* given input stream.
*
* @param in The stream to read from.
* @throws IOException In case the stream cannot be opened or another IO error occurs.
*/
public ModSecurity2AuditStream( InputStream in, AuditEventListener listener, boolean persist )
throws IOException
{
super( in );
this.listener = listener;
this.tail = persist;
}
/**
* @see org.jwall.web.audit.io.AuditEventReader#readNext()
*/
public AuditEvent readNext() throws IOException, ParseException, EOFException
{
//log.info( "readEvent " + counter );
counter++;
String line = readLine(); // this holds the current line to be worked on
long offset = (long) getDataRead();
// this array of strings is filled with the appropriate section-strings
// stringbuffers are needed because the sections are read one line at a time
StringBuffer[] sections = new StringBuffer[ModSecurity.SECTIONS.length()]; // at maximum 26 sections are possible;
for(int i = 0; i < sections.length; i++)
sections[i] = new StringBuffer();
// ptr always points to the stringbuffer which is to be read
int ptr = 0;
try {
//
// we skip lines until we found an audit-start-line, which has the form "--[0-9a-f]*-A--"
// if the reader hits EOF => line is null and an exception will be thrown, catched below and null is returned
//
while((! line.matches("--[\\-\\@0-9A-Za-z]*-A--"))){
line = readLine();
bytesRead += line.length() + 1.0d;
}
String id = line.replaceFirst("--", "").replaceAll("-A--", "");
//
// ok, now line points to the beginning-separator-line of the audit event.
//
while(!(line.startsWith("--") && line.endsWith("-Z--"))){
//log.info( "line: {}", line ); // this is the section-beginning-marker
ptr = getSectionIndex(line);
line = readLine(); // now line is the first line of the sections body
bytesRead += line.length() + 1.0d;
//
// if ptr is -1 an invalid section-name was found !
//
if(ptr >= 0){
//
// no we read a section, until a new one begins
//
sections[ptr] = new StringBuffer();
do {
sections[ptr].append(line+"\n");
line = readLine();
bytesRead += line.length();
//log.info("line: {}", line);
} while(!( line.trim().matches("^--.*-[A-Z]--$") ) );
} else {
//
// invalid section-name
//
log.debug( "Line contains invalid section-name: " + line );
}
}
//
// We need to convert the stringBuffer-array into a string-array
// as the AuditEvent-constructor expects a string-array
//
String[] data = new String[ModSecurity.SECTIONS.length()];
for(int i = 0; i < data.length; i++)
data[i] = sections[i].toString();
//
// the A-section is empty - it seems that the EOF has been reached
//
if(data[0].equals(""))
return null;
AuditEvent event = eventFactory.createAuditEvent( id, data, inputFile, offset, (long) bytesRead - offset, AuditEventType.ModSecurity2 );
event.set( AuditEvent.SENSOR_NAME, sensor );
return event;
} catch ( EOFException eof ) {
System.out.println( "End-of-file reached!" );
eofReached = true;
throw eof;
} catch ( Exception e ) {
e.printStackTrace();
return null;
}
}
private String readLine() throws IOException {
String line = reader.readLine();
if( line == null )
throw new EOFException( "End of file reached!");
if( prefix == "" )
return line;
int idx = line.indexOf( ": " );
if( idx > 0 ){
prefix = ": ";
int b = idx;
while( b > 0 && line.charAt( b ) != ' ')
b--;
sensor = line.substring( b, idx );
return line.substring( idx + 2 );
} else
prefix = "";
return line;
}
@Override
public void run() {
while( true ){
try {
AuditEvent event = readNext();
listener.eventArrived( event );
} catch (Exception e) {
e.printStackTrace();
//System.exit(0);
return;
}
}
}
@Override
public Iterator<AuditEvent> iterator()
{
try {
return new AuditEventIterator( this );
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}