/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright The OWASP ZAP Development Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.zap.db.sql;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.db.Database;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.db.DbUtils;
import org.parosproxy.paros.db.RecordHistory;
import org.parosproxy.paros.db.TableHistory;
import org.parosproxy.paros.extension.option.DatabaseParam;
import org.parosproxy.paros.model.HistoryReference;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpStatusCode;
public class SqlTableHistory extends SqlAbstractTable implements TableHistory {
private static final String TABLE_NAME = DbSQL.getSQL("history.table_name");
private static final String HISTORYID = DbSQL.getSQL("history.field.historyid");
private static final String SESSIONID = DbSQL.getSQL("history.field.sessionid");
private static final String HISTTYPE = DbSQL.getSQL("history.field.histtype");
private static final String STATUSCODE = DbSQL.getSQL("history.field.statuscode");
private static final String TIMESENTMILLIS = DbSQL.getSQL("history.field.timesentmillis");
private static final String TIMEELAPSEDMILLIS = DbSQL.getSQL("history.field.timeelapsedmillis");
private static final String REQHEADER = DbSQL.getSQL("history.field.reqheader");
private static final String REQBODY = DbSQL.getSQL("history.field.reqbody");
private static final String RESHEADER = DbSQL.getSQL("history.field.resheader");
private static final String RESBODY = DbSQL.getSQL("history.field.resbody");
private static final String TAG = DbSQL.getSQL("history.field.tag");
private static final String NOTE = DbSQL.getSQL("history.field.note");
private static final String RESPONSE_FROM_TARGET_HOST = DbSQL.getSQL("history.field.responsefromtargethost");
private int lastInsertedIndex;
private static boolean isExistStatusCode = false;
// ZAP: Added logger
private static final Logger log = Logger.getLogger(SqlTableHistory.class);
private boolean bodiesAsBytes;
public SqlTableHistory() {
}
//ZAP: Allow the request and response body sizes to be user-specifiable as far as possible
int configuredrequestbodysize = -1;
int configuredresponsebodysize = -1;
@Override
protected void reconnect(Connection conn) throws DatabaseException {
try {
//ZAP: Allow the request and response body sizes to be user-specifiable as far as possible
//re-load the configuration data from file, to get the configured length of the request and response bodies
//this will later be compared to the actual lengths of these fields in the database (in updateTable(Connection c))
DatabaseParam dbparams = new DatabaseParam ();
dbparams.load(Constant.getInstance().FILE_CONFIG);
this.configuredrequestbodysize = dbparams.getRequestBodySize();
this.configuredresponsebodysize = dbparams.getResponseBodySize();
bodiesAsBytes = true;
if (DbSQL.getDbType().equals(Database.DB_TYPE_HSQLDB)) {
updateTable(conn);
}
isExistStatusCode = DbUtils.hasColumn(conn, TABLE_NAME, STATUSCODE);
int currentIndex = 0;
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(DbSQL.getSQL("history.ps.lastindex"));
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
currentIndex = rs.getInt(1);
}
}
} finally {
if (stmt != null) {
try {
stmt.close();
} catch(SQLException e) {
if (log.isDebugEnabled()) {
log.debug(e.getMessage(), e);
}
}
}
}
lastInsertedIndex = currentIndex;
} catch (SQLException e) {
throw new DatabaseException(e);
}
}
// ZAP: Added the method.
private void updateTable(Connection connection) throws DatabaseException {
try {
if (!DbUtils.hasColumn(connection, TABLE_NAME, TAG)) {
DbUtils.executeAndClose(connection.prepareStatement(DbSQL.getSQL("history.ps.addtag")));
}
// Add the NOTE column to the db if necessary
if (!DbUtils.hasColumn(connection, TABLE_NAME, NOTE)) {
DbUtils.executeAndClose(connection.prepareStatement(DbSQL.getSQL("history.ps.addnote")));
}
/* TODO how to handle HSQLDB dependancy?? Need to parameterize somehow.. vvvvvvvvvvvv */
if (DbUtils.getColumnType(connection, TABLE_NAME, REQBODY) != 61 /*Types.SQL_VARBINARY*/) {
bodiesAsBytes = false;
} else {
// Databases created with ZAP<1.4.0.1 used VARCHAR for the REQBODY/RESBODY
// HSQLDB 1.8.x converted from VARCHAR to bytes without problems
// (through the method ResultSet.getBytes)
// but the new version doesn't, it throws the following exception:
// incompatible data type in conversion: from SQL type VARCHAR
}
if (!DbUtils.hasColumn(connection, TABLE_NAME, RESPONSE_FROM_TARGET_HOST)) {
DbUtils.executeAndClose(connection.prepareStatement(DbSQL.getSQL("history.ps.addrespfromtarget")));
DbUtils.executeUpdateAndClose(connection.prepareStatement(DbSQL.getSQL("history.ps.setrespfromtarget")));
}
int requestbodysizeindb = DbUtils.getColumnSize(connection, TABLE_NAME, REQBODY);
int responsebodysizeindb = DbUtils.getColumnSize(connection, TABLE_NAME, RESBODY);
try {
if (requestbodysizeindb != this.configuredrequestbodysize && this.configuredrequestbodysize > 0) {
PreparedStatement stmt = connection.prepareStatement(DbSQL.getSQL("history.ps.changereqsize"));
stmt.setInt(1, this.configuredrequestbodysize);
DbUtils.executeAndClose(stmt);
}
if (responsebodysizeindb != this.configuredresponsebodysize && this.configuredresponsebodysize > 0) {
PreparedStatement stmt = connection.prepareStatement(DbSQL.getSQL("history.ps.changerespsize"));
stmt.setInt(1, this.configuredresponsebodysize);
DbUtils.executeAndClose(stmt);
}
}
catch (SQLException e) {
log.error("An error occurred while modifying a column length on "+ TABLE_NAME);
log.error("The 'Maximum Request Body Size' value in the Database Options needs to be set to at least " + requestbodysizeindb + " to avoid this error" );
log.error("The 'Maximum Response Body Size' value in the Database Options needs to be set to at least " + responsebodysizeindb + " to avoid this error" );
log.error("The SQL Exception was:", e);
throw e;
}
} catch (SQLException e) {
throw new DatabaseException(e);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#read(int)
*/
@Override
public RecordHistory read(int historyId) throws HttpMalformedHeaderException, DatabaseException {
SqlPreparedStatementWrapper psRead = null;
try {
psRead = DbSQL.getSingleton().getPreparedStatement( "history.ps.read");
psRead.getPs().setInt(1, historyId);
psRead.getPs().execute();
RecordHistory result = null;
try (ResultSet rs = psRead.getPs().getResultSet()) {
result = build(rs);
}
return result;
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
DbSQL.getSingleton().releasePreparedStatement(psRead);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#write(long, int, org.parosproxy.paros.network.HttpMessage)
*/
@Override
public RecordHistory write(long sessionId, int histType, HttpMessage msg) throws HttpMalformedHeaderException, DatabaseException {
String reqHeader = "";
byte[] reqBody = new byte[0];
String resHeader = "";
byte[] resBody = reqBody;
String method = "";
String uri = "";
int statusCode = 0;
String note = msg.getNote();
if (!msg.getRequestHeader().isEmpty()) {
reqHeader = msg.getRequestHeader().toString();
reqBody = msg.getRequestBody().getBytes();
method = msg.getRequestHeader().getMethod();
uri = msg.getRequestHeader().getURI().toString();
}
if (!msg.getResponseHeader().isEmpty()) {
resHeader = msg.getResponseHeader().toString();
resBody = msg.getResponseBody().getBytes();
statusCode = msg.getResponseHeader().getStatusCode();
}
//return write(sessionId, histType, msg.getTimeSentMillis(), msg.getTimeElapsedMillis(), method, uri, statusCode, reqHeader, reqBody, resHeader, resBody, msg.getTag());
return write(sessionId, histType, msg.getTimeSentMillis(), msg.getTimeElapsedMillis(), method, uri, statusCode, reqHeader, reqBody, resHeader, resBody, null, note, msg.isResponseFromTargetHost());
}
private RecordHistory write(long sessionId, int histType, long timeSentMillis, int timeElapsedMillis,
String method, String uri, int statusCode,
String reqHeader, byte[] reqBody, String resHeader, byte[] resBody, String tag, String note, boolean responseFromTargetHost) throws HttpMalformedHeaderException, DatabaseException {
//ZAP: Allow the request and response body sizes to be user-specifiable as far as possible
if (reqBody.length > this.configuredrequestbodysize) {
throw new DatabaseException("The actual Request Body length "+ reqBody.length + " is greater than the configured request body length "+ this.configuredrequestbodysize);
}
if (resBody.length > this.configuredresponsebodysize) {
throw new DatabaseException("The actual Response Body length "+ resBody.length + " is greater than the configured response body length "+ this.configuredresponsebodysize);
}
SqlPreparedStatementWrapper psInsert = null;
try {
psInsert = DbSQL.getSingleton().getPreparedStatement( "history.ps.insertstd");
psInsert.getPs().setLong(1, sessionId);
psInsert.getPs().setInt(2, histType);
psInsert.getPs().setLong(3, timeSentMillis);
psInsert.getPs().setInt(4, timeElapsedMillis);
psInsert.getPs().setString(5, method);
psInsert.getPs().setString(6, uri);
psInsert.getPs().setString(7, reqHeader);
if (bodiesAsBytes) {
psInsert.getPs().setBytes(8, reqBody);
} else {
psInsert.getPs().setString(8, new String(reqBody, StandardCharsets.US_ASCII));
}
psInsert.getPs().setString(9, resHeader);
if (bodiesAsBytes) {
psInsert.getPs().setBytes(10, resBody);
} else {
psInsert.getPs().setString(10, new String(resBody, StandardCharsets.US_ASCII));
}
psInsert.getPs().setString(11, tag);
// ZAP: Added the statement.
int currentIdx = 12;
if (isExistStatusCode) {
psInsert.getPs().setInt(currentIdx, statusCode);
// ZAP: Added the statement.
++currentIdx;
}
// ZAP: Added the statement.
psInsert.getPs().setString(currentIdx, note);
++currentIdx;
psInsert.getPs().setBoolean(currentIdx, responseFromTargetHost);
psInsert.getPs().executeUpdate();
try (ResultSet rs = psInsert.getLastInsertedId()) {
rs.next();
int id = rs.getInt(1);
lastInsertedIndex = id;
return read(id);
}
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
DbSQL.getSingleton().releasePreparedStatement(psInsert);
}
}
private RecordHistory build(ResultSet rs) throws HttpMalformedHeaderException, DatabaseException {
try {
RecordHistory history = null;
try {
if (rs.next()) {
byte[] reqBody;
byte[] resBody;
if (bodiesAsBytes) {
reqBody = rs.getBytes(REQBODY);
resBody = rs.getBytes(RESBODY);
} else {
reqBody = rs.getString(REQBODY).getBytes();
resBody = rs.getString(RESBODY).getBytes();
}
history = new RecordHistory(
rs.getInt(HISTORYID),
rs.getInt(HISTTYPE),
rs.getLong(SESSIONID),
rs.getLong(TIMESENTMILLIS),
rs.getInt(TIMEELAPSEDMILLIS),
rs.getString(REQHEADER),
reqBody,
rs.getString(RESHEADER),
resBody,
rs.getString(TAG),
rs.getString(NOTE), // ZAP: Added note
rs.getBoolean(RESPONSE_FROM_TARGET_HOST)
);
}
} finally {
rs.close();
}
return history;
} catch (SQLException e) {
throw new DatabaseException(e);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#getHistoryIds(long)
*/
@Override
public List<Integer> getHistoryIds(long sessionId) throws DatabaseException {
SqlPreparedStatementWrapper psGetAllHistoryIds = null;
try {
psGetAllHistoryIds = DbSQL.getSingleton().getPreparedStatement( "history.ps.gethistoryids");
List<Integer> v = new ArrayList<>();
psGetAllHistoryIds.getPs().setLong(1, sessionId);
try (ResultSet rs = psGetAllHistoryIds.getPs().executeQuery()) {
while (rs.next()) {
v.add(Integer.valueOf(rs.getInt(HISTORYID)));
}
}
return v;
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
DbSQL.getSingleton().releasePreparedStatement(psGetAllHistoryIds);
}
//return getHistoryIdsOfHistType(sessionId, null);
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#getHistoryIdsOfHistType(long, int)
*/
@Override
public List<Integer> getHistoryIdsOfHistType(long sessionId, int... histTypes) throws DatabaseException {
if (histTypes == null || histTypes.length == 0) {
return getHistoryIds(sessionId);
}
SqlPreparedStatementWrapper psGetAllHistoryIdsIncTypes = null;
try {
psGetAllHistoryIdsIncTypes = DbSQL.getSingleton().getPreparedStatement( "history.ps.gethistoryidsinctypes");
List<Integer> v = new ArrayList<>();
psGetAllHistoryIdsIncTypes.getPs().setLong(1, sessionId);
DbSQL.setSetValues(psGetAllHistoryIdsIncTypes.getPs(), 2, histTypes);
try (ResultSet rs = psGetAllHistoryIdsIncTypes.getPs().executeQuery()) {
while (rs.next()) {
v.add(Integer.valueOf(rs.getInt(HISTORYID)));
}
}
return v;
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
DbSQL.getSingleton().releasePreparedStatement(psGetAllHistoryIdsIncTypes);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#getHistoryIdsExceptOfHistType(long, int)
*/
@Override
public List<Integer> getHistoryIdsExceptOfHistType(long sessionId, int... histTypes) throws DatabaseException {
if (histTypes == null || histTypes.length == 0) {
return getHistoryIds(sessionId);
}
SqlPreparedStatementWrapper psGetAllHistoryIdsExcTypes = null;
try {
List<Integer> v = new ArrayList<>();
psGetAllHistoryIdsExcTypes = DbSQL.getSingleton().getPreparedStatement( "history.ps.gethistoryidsnottypes");
psGetAllHistoryIdsExcTypes.getPs().setLong(1, sessionId);
DbSQL.setSetValues(psGetAllHistoryIdsExcTypes.getPs(), 2, histTypes);
try (ResultSet rs = psGetAllHistoryIdsExcTypes.getPs().executeQuery()) {
while (rs.next()) {
v.add(Integer.valueOf(rs.getInt(HISTORYID)));
}
}
return v;
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
DbSQL.getSingleton().releasePreparedStatement(psGetAllHistoryIdsExcTypes);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#getHistoryList(long, int, java.lang.String, boolean)
*/
@Override
public List<Integer> getHistoryList(long sessionId, int histType, String filter, boolean isRequest) throws DatabaseException {
try { // TODO
PreparedStatement psReadSearch = getConnection().prepareStatement("SELECT * FROM HISTORY WHERE " + SESSIONID + " = ? AND " + HISTTYPE + " = ? ORDER BY " + HISTORYID);
ResultSet rs = null;
Vector<Integer> v = new Vector<>();
try {
Pattern pattern = Pattern.compile(filter, Pattern.MULTILINE| Pattern.CASE_INSENSITIVE);
Matcher matcher = null;
psReadSearch.setLong(1, sessionId);
psReadSearch.setInt(2, histType);
rs = psReadSearch.executeQuery();
while (rs.next()) {
if (isRequest) {
matcher = pattern.matcher(rs.getString(REQHEADER));
if (matcher.find()) {
// ZAP: Changed to use the method Integer.valueOf.
v.add(Integer.valueOf(rs.getInt(HISTORYID)));
continue;
}
matcher = pattern.matcher(rs.getString(REQBODY));
if (matcher.find()) {
// ZAP: Changed to use the method Integer.valueOf.
v.add(Integer.valueOf(rs.getInt(HISTORYID)));
continue;
}
} else {
matcher = pattern.matcher(rs.getString(RESHEADER));
if (matcher.find()) {
// ZAP: Changed to use the method Integer.valueOf.
v.add(Integer.valueOf(rs.getInt(HISTORYID)));
continue;
}
matcher = pattern.matcher(rs.getString(RESBODY));
if (matcher.find()) {
// ZAP: Changed to use the method Integer.valueOf.
v.add(Integer.valueOf(rs.getInt(HISTORYID)));
continue;
}
}
}
} finally {
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
// Ignore
}
}
psReadSearch.close();
}
return v;
} catch (SQLException e) {
throw new DatabaseException(e);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#deleteHistorySession(long)
*/
@Override
public void deleteHistorySession(long sessionId) throws DatabaseException {
SqlPreparedStatementWrapper psDeleteSession = null;
try {
psDeleteSession = DbSQL.getSingleton().getPreparedStatement( "history.ps.deletesession");
psDeleteSession.getPs().setLong(1, sessionId);
psDeleteSession.getPs().executeUpdate();
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
DbSQL.getSingleton().releasePreparedStatement(psDeleteSession);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#deleteHistoryType(long, int)
*/
@Override
public void deleteHistoryType(long sessionId, int historyType) throws DatabaseException {
SqlPreparedStatementWrapper psDeleteType = null;
try {
psDeleteType = DbSQL.getSingleton().getPreparedStatement( "history.ps.deletetype");
psDeleteType.getPs().setLong(1, sessionId);
psDeleteType.getPs().setInt(2, historyType);
psDeleteType.getPs().executeUpdate();
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
DbSQL.getSingleton().releasePreparedStatement(psDeleteType);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#delete(int)
*/
@Override
public void delete(int historyId) throws DatabaseException {
SqlPreparedStatementWrapper psDelete = null;
try {
psDelete = DbSQL.getSingleton().getPreparedStatement( "history.ps.delete");
psDelete.getPs().setInt(1, historyId);
psDelete.getPs().executeUpdate();
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
DbSQL.getSingleton().releasePreparedStatement(psDelete);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#delete(java.util.List)
*/
// ZAP: Added method.
@Override
public void delete(List<Integer> ids) throws DatabaseException {
delete(ids, 1000);
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#delete(java.util.List, int)
*/
@Override
public void delete(List<Integer> ids, int batchSize) throws DatabaseException {
if (ids == null) {
throw new IllegalArgumentException("Parameter ids must not be null.");
}
if (batchSize <= 0) {
throw new IllegalArgumentException("Parameter batchSize must be greater than zero.");
}
SqlPreparedStatementWrapper psDelete = null;
try {
psDelete = DbSQL.getSingleton().getPreparedStatement( "history.ps.delete");
int count = 0;
for (Integer id : ids) {
psDelete.getPs().setInt(1, id.intValue());
psDelete.getPs().addBatch();
count++;
if (count % batchSize == 0) {
psDelete.getPs().executeBatch();
count = 0;
}
}
if (count % batchSize != 0) {
psDelete.getPs().executeBatch();
}
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
DbSQL.getSingleton().releasePreparedStatement(psDelete);
}
}
/**
* @deprecated (2.5.0) Use {@link HistoryReference#addTemporaryType(int)} instead.
* @since 2.4
* @param historyType the history type that will be set as temporary
* @see #deleteTemporary()
*/
@Deprecated
public static void setHistoryTypeAsTemporary(int historyType) {
}
/**
* @deprecated (2.5.0) Use {@link HistoryReference#removeTemporaryType(int)} instead.
* @since 2.4
* @param historyType the history type that will be marked as temporary
* @see #deleteTemporary()
*/
@Deprecated
public static void unsetHistoryTypeAsTemporary(int historyType) {
HistoryReference.removeTemporaryType(historyType);
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#deleteTemporary()
*/
@Override
public void deleteTemporary() throws DatabaseException {
SqlPreparedStatementWrapper psDeleteTemp = null;
try {
psDeleteTemp = DbSQL.getSingleton().getPreparedStatement( "history.ps.deletetemp");
for (Integer type : HistoryReference.getTemporaryTypes()) {
psDeleteTemp.getPs().setInt(1, type);
psDeleteTemp.getPs().execute();
}
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
DbSQL.getSingleton().releasePreparedStatement(psDeleteTemp);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#containsURI(long, int, java.lang.String, java.lang.String, byte[])
*/
@Override
public boolean containsURI(long sessionId, int historyType, String method, String uri, byte[] body) throws DatabaseException {
SqlPreparedStatementWrapper psContainsURI = null;
try {
psContainsURI = DbSQL.getSingleton().getPreparedStatement( "history.ps.containsuri");
psContainsURI.getPs().setString(1, uri);
psContainsURI.getPs().setString(2, method);
if (bodiesAsBytes) {
psContainsURI.getPs().setBytes(3, body);
} else {
psContainsURI.getPs().setString(3, new String(body));
}
psContainsURI.getPs().setLong(4, sessionId);
psContainsURI.getPs().setInt(5, historyType);
try (ResultSet rs = psContainsURI.getPs().executeQuery()) {
if (rs.next()) {
return true;
}
}
return false;
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
DbSQL.getSingleton().releasePreparedStatement(psContainsURI);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#getHistoryCache(org.parosproxy.paros.model.HistoryReference, org.parosproxy.paros.network.HttpMessage)
*/
@Override
public RecordHistory getHistoryCache(HistoryReference ref, HttpMessage reqMsg) throws DatabaseException , HttpMalformedHeaderException {
try {
// get the cache from provided reference.
// naturally, the obtained cache should be AFTER AND NEARBY to the given reference.
// - historyId up to historyId+200
// - match sessionId
// - history type can be MANUEL or hidden (hidden is used by images not explicitly stored in history)
// - match URI
PreparedStatement psReadCache = null;
// TODO
if (isExistStatusCode) {
// psReadCache = getConnection().prepareStatement("SELECT TOP 1 * FROM HISTORY WHERE URI = ? AND METHOD = ? AND REQBODY = ? AND " + HISTORYID + " >= ? AND " + HISTORYID + " <= ? AND SESSIONID = ? AND (HISTTYPE = " + HistoryReference.TYPE_MANUAL + " OR HISTTYPE = " + HistoryReference.TYPE_HIDDEN + ") AND STATUSCODE != 304");
psReadCache = getConnection().prepareStatement("SELECT TOP 1 * FROM HISTORY WHERE URI = ? AND METHOD = ? AND REQBODY = ? AND " + HISTORYID + " >= ? AND " + HISTORYID + " <= ? AND SESSIONID = ? AND STATUSCODE != 304");
} else {
// psReadCache = getConnection().prepareStatement("SELECT * FROM HISTORY WHERE URI = ? AND METHOD = ? AND REQBODY = ? AND " + HISTORYID + " >= ? AND " + HISTORYID + " <= ? AND SESSIONID = ? AND (HISTTYPE = " + HistoryReference.TYPE_MANUAL + " OR HISTTYPE = " + HistoryReference.TYPE_HIDDEN + ")");
psReadCache = getConnection().prepareStatement("SELECT * FROM HISTORY WHERE URI = ? AND METHOD = ? AND REQBODY = ? AND " + HISTORYID + " >= ? AND " + HISTORYID + " <= ? AND SESSIONID = ?)");
}
psReadCache.setString(1, reqMsg.getRequestHeader().getURI().toString());
psReadCache.setString(2, reqMsg.getRequestHeader().getMethod());
if (bodiesAsBytes) {
psReadCache.setBytes(3, reqMsg.getRequestBody().getBytes());
} else {
psReadCache.setString(3, new String(reqMsg.getRequestBody().getBytes()));
}
psReadCache.setInt(4, ref.getHistoryId());
psReadCache.setInt(5, ref.getHistoryId()+200);
psReadCache.setLong(6, ref.getSessionId());
ResultSet rs = psReadCache.executeQuery();
RecordHistory rec = null;
try {
do {
rec = build(rs);
// for retrieval from cache, the message requests nature must be the same.
// and the result should NOT be NOT_MODIFIED for rendering by browser
if (rec != null && rec.getHttpMessage().equals(reqMsg) &&
rec.getHttpMessage().getResponseHeader().getStatusCode() != HttpStatusCode.NOT_MODIFIED) {
return rec;
}
} while (rec != null);
} finally {
try {
rs.close();
psReadCache.close();
} catch (Exception e) {
// ZAP: Log exceptions
log.warn(e.getMessage(), e);
}
}
// if cache not exist, probably due to NOT_MODIFIED,
// lookup from cache BEFORE the given reference
// TODO
if (isExistStatusCode) {
psReadCache = getConnection().prepareStatement("SELECT TOP 1 * FROM HISTORY WHERE URI = ? AND METHOD = ? AND REQBODY = ? AND SESSIONID = ? AND STATUSCODE != 304");
} else {
psReadCache = getConnection().prepareStatement("SELECT * FROM HISTORY WHERE URI = ? AND METHOD = ? AND REQBODY = ? AND SESSIONID = ?");
}
psReadCache.setString(1, reqMsg.getRequestHeader().getURI().toString());
psReadCache.setString(2, reqMsg.getRequestHeader().getMethod());
if (bodiesAsBytes) {
psReadCache.setBytes(3, reqMsg.getRequestBody().getBytes());
} else {
psReadCache.setString(3, new String(reqMsg.getRequestBody().getBytes()));
}
psReadCache.setLong(4, ref.getSessionId());
rs = psReadCache.executeQuery();
rec = null;
try {
do {
rec = build(rs);
if (rec != null && rec.getHttpMessage().equals(reqMsg) && rec.getHttpMessage().getResponseHeader().getStatusCode() != HttpStatusCode.NOT_MODIFIED) {
return rec;
}
} while (rec != null);
} finally {
try {
rs.close();
psReadCache.close();
} catch (Exception e) {
// ZAP: Log exceptions
log.warn(e.getMessage(), e);
}
}
return null;
} catch (SQLException e) {
throw new DatabaseException(e);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#updateNote(int, java.lang.String)
*/
@Override
public void updateNote(int historyId, String note) throws DatabaseException {
SqlPreparedStatementWrapper psUpdateNote = null;
try {
psUpdateNote = DbSQL.getSingleton().getPreparedStatement( "history.ps.updatenote");
psUpdateNote.getPs().setString(1, note);
psUpdateNote.getPs().setInt(2, historyId);
psUpdateNote.getPs().execute();
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
DbSQL.getSingleton().releasePreparedStatement(psUpdateNote);
}
}
/* (non-Javadoc)
* @see org.parosproxy.paros.db.TbleHistoryIf#lastIndex()
*/
@Override
public int lastIndex () {
return lastInsertedIndex;
}
}