/* * Zed Attack Proxy (ZAP) and its related class files. * * ZAP is an HTTP/HTTPS proxy for assessing web application security. * * Copyright 2010 psiinon@gmail.com * * 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.extension.httppanel.view.hex; import java.util.ArrayList; import java.util.List; import javax.swing.table.AbstractTableModel; import org.zaproxy.zap.utils.ByteBuilder; public class HttpPanelHexModel extends AbstractTableModel { private static final String[] hexSymbols = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; private static final int BITS_PER_HEX_DIGIT = 4; private static final int COLUMN_COUNT = 34; private static final long serialVersionUID = 1L; private List<String[]> listRow = new ArrayList<>(); private boolean editable = false; private boolean changed = false; public boolean isEditable() { return editable; } public void setEditable(boolean editable) { this.editable = editable; } public HttpPanelHexModel() { super(); } @Override public int getColumnCount() { return COLUMN_COUNT; } @Override public int getRowCount() { return listRow.size(); } @Override public Object getValueAt(int row, int col) { return listRow.get(row)[col]; } private boolean isHexColumn (int col) { return col > 0 && col <= 16; } private boolean isCharColumn (int col) { return col > 17; } @Override public String getColumnName(int col) { if (isHexColumn(col)) { return Integer.toHexString(col-1); } else if (isCharColumn(col)) { return Integer.toHexString(col-18); } return ""; } public static String toHexFromByte(final byte b) { byte leftSymbol = (byte)((b >>> BITS_PER_HEX_DIGIT) & 0x0f); byte rightSymbol = (byte)(b & 0x0f); return (hexSymbols[leftSymbol] + hexSymbols[rightSymbol]); } private String [] newRow() { String [] row = new String[COLUMN_COUNT]; row[0] = String.format("%08x", (listRow.size() * 16)); return row; } private void setHex(String[] row, int index, byte b) { row[index] = toHexFromByte(b); row[index + 17] = String.valueOf((char)b); } private void setChar(String[] row, int index, char c) { row[index - 17] = toHexFromByte((byte)c); row[index] = String.valueOf(c); } public synchronized void setData(byte[] body) { listRow.clear(); if (body.length != 0 || isEditable()) { int index = 0; int rowIndex = 0; String [] row = newRow(); listRow.add(row); while (index < body.length) { setHex(row, rowIndex + 1, body[index]); rowIndex++; if (rowIndex >= 16) { row = newRow(); listRow.add(row); rowIndex = 0; } index++; } } this.fireTableDataChanged(); changed = false; } public synchronized byte[] getData() { // Need to implement if/when edit supported ByteBuilder bb = new ByteBuilder(); for (String[] row : listRow) { for (int i=1; i < 17; i++) { if (row[i] == null || row[i].length() == 0) { break; } bb.append((char)Integer.parseInt(row[i], 16)); } } return bb.toByteArray(); } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { if (! isEditable()) { return false; } if (! (isHexColumn(columnIndex) || isCharColumn(columnIndex))) { // Either the index or spacer column return false; } if (rowIndex < listRow.size() - 1) { // Not the last row, so no problem return true; } String[] row = listRow.get(rowIndex); if (!isCharColumn(columnIndex)) { // Previous cell is null - can only edit the first null cell return (row[columnIndex-1] != null); } else if (row[columnIndex-1] == null && columnIndex != 18) { // Previous cell is null and it's not the first text cell return false; } return true; } @Override public void setValueAt(Object value, int row, int col) { String val = (String) value; byte byteVal = -1; char charVal = (char)0; if (isHexColumn(col)) { if (val.length() > 2) { return; } try { byteVal = (byte)Integer.parseInt(val, 16); } catch (NumberFormatException e) { return; } } else if (isCharColumn(col)) { if (val.length() != 1) { return; } charVal = val.charAt(0); } else { return; } String[] cell = listRow.get(row); if (isHexColumn(col)) { setHex(cell, col, byteVal); } else { setChar(cell, col, charVal); } int lastRowChanged = row; if ((row == listRow.size() - 1) && cell[16] != null) { // Last cell on last line is used, add a new row listRow.add(newRow()); lastRowChanged++; } fireTableRowsUpdated(row, lastRowChanged); changed = true; } public boolean hasChanged() { return changed; } }