/***********************************************************************
*
* $CVSHeader$
*
* This file is part of WebScarab, an Open Web Application Security
* Project utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 2004 Rogan Dawes
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This program 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Getting Source
* ==============
*
* Source for this application is maintained at Sourceforge.net, a
* repository for free software projects.
*
* For details, please see http://www.sourceforge.net/projects/owasp
*
*/
/*
* HexEditor.java
*
* Created on November 4, 2003, 8:23 AM
*/
package org.owasp.webscarab.ui.swing.editors;
import javax.swing.table.DefaultTableCellRenderer;
import org.owasp.webscarab.model.Preferences;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumnModel;
import java.awt.Font;
import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.Event;
import javax.swing.AbstractAction;
import java.awt.event.ActionEvent;
import javax.swing.SwingUtilities;
import javax.swing.KeyStroke;
import javax.swing.InputMap;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
*
* @author rdawes
*/
public class HexPanel extends javax.swing.JPanel implements ByteArrayEditor {
/**
*
*/
private static final long serialVersionUID = -6389071746723775943L;
private HexTableModel _tableModel = null;
private boolean _editable = false;
private int _columns = 16;
private boolean _modified = false;
private byte[] _data = null;
private byte[] _original = null;
/** Creates new form HexEditor */
public HexPanel() {
initComponents();
setName("Hex");
_tableModel = new HexTableModel(_columns);
hexTable.setModel(_tableModel);
hexTable.setFont(new Font("Monospaced", Font.PLAIN, 12));
hexTable.getTableHeader().setReorderingAllowed(false);
DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
renderer.putClientProperty("html.disable", Boolean.TRUE);
hexTable.setDefaultRenderer(Object.class, renderer);
TableColumnModel colModel = hexTable.getColumnModel();
// FIXME : use FontMetrics to get the real width of the font
for (int i=0; i<_columns+2; i++) {
colModel.getColumn(i).setPreferredWidth(2*9);
colModel.getColumn(i).setResizable(false);
}
colModel.getColumn(0).setPreferredWidth(8*9);
colModel.getColumn(_columns+1).setPreferredWidth(_columns*9);
InputMap im = getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.CTRL_MASK), "Save");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_L, Event.CTRL_MASK), "Load");
getActionMap().put("Save", new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = 6229743807139403588L;
public void actionPerformed(ActionEvent evt) {
JFileChooser jfc = new JFileChooser(Preferences.getPreference("WebScarab.DefaultDirectory"));
jfc.setDialogTitle("Select a file to write the message content to");
int returnVal = jfc.showSaveDialog(HexPanel.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
try {
FileOutputStream fos = new FileOutputStream(jfc.getSelectedFile());
fos.write(_data);
fos.close();
} catch (IOException ioe) {
JOptionPane.showMessageDialog(HexPanel.this, "Error writing file: " + ioe.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
Preferences.setPreference("WebScarab.DefaultDirectory", jfc.getCurrentDirectory().getAbsolutePath());
}
});
getActionMap().put("Load", new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = 2208374207129449438L;
public void actionPerformed(ActionEvent evt) {
if (!_editable) return;
JFileChooser jfc = new JFileChooser(Preferences.getPreference("WebScarab.DefaultDirectory"));
jfc.setDialogTitle("Select a file to read the message content from");
int returnVal = jfc.showOpenDialog(HexPanel.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
try {
FileInputStream fis = new FileInputStream(jfc.getSelectedFile());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[2048];
int got;
while ((got = fis.read(buff))>0) {
baos.write(buff,0,got);
}
fis.close();
baos.close();
_data = baos.toByteArray();
_tableModel.fireTableDataChanged();
_modified = true;
} catch (IOException ioe) {
JOptionPane.showMessageDialog(HexPanel.this, "Error writing file: " + ioe.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
Preferences.setPreference("WebScarab.DefaultDirectory", jfc.getCurrentDirectory().getAbsolutePath());
}
});
}
public String[] getContentTypes() {
return new String[] { ".*" };
}
public void setEditable(boolean editable) {
_editable = editable;
hexTable.setBackground(editable ? Color.WHITE : Color.LIGHT_GRAY );
// we could do things like make insert and delete buttons visible and invisible here
}
public void setBytes(String contentType, byte[] bytes) {
_original = bytes;
if (bytes == null) {
_data = null;
} else {
_data = new byte[bytes.length];
System.arraycopy(bytes, 0, _data, 0, bytes.length);
}
if (SwingUtilities.isEventDispatchThread()) {
_tableModel.fireTableDataChanged();
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_tableModel.fireTableDataChanged();
}
});
}
_modified = false;
}
public boolean isModified() {
if (hexTable.isEditing()) hexTable.getCellEditor().stopCellEditing();
return _editable && _modified;
}
public byte[] getBytes() {
if (_editable && isModified()) _original = _data;
_modified = false;
return _original;
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
jScrollPane1 = new javax.swing.JScrollPane();
hexTable = new javax.swing.JTable();
setLayout(new java.awt.GridBagLayout());
setPreferredSize(new java.awt.Dimension(453, 20));
jScrollPane1.setMinimumSize(new java.awt.Dimension(22, 48));
hexTable.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
},
new String [] {
}
));
hexTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
jScrollPane1.setViewportView(hexTable);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(jScrollPane1, gridBagConstraints);
}
// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTable hexTable;
private javax.swing.JScrollPane jScrollPane1;
// End of variables declaration//GEN-END:variables
public static void main(String[] args) {
javax.swing.JFrame top = new javax.swing.JFrame("Hex Editor");
top.addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt) {
System.exit(0);
}
});
HexPanel hp = new HexPanel();
top.getContentPane().add(hp);
top.setBounds(100,100,600,400);
try {
hp.setBytes(null, new byte[] {
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
});
hp.setEditable(true);
top.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
private class HexTableModel extends AbstractTableModel {
/**
*
*/
private static final long serialVersionUID = -5393432450731234710L;
private int _columns = 8;
public HexTableModel() {
}
public HexTableModel(int columns) {
_columns = columns;
}
public String getColumnName(int columnIndex) {
if (columnIndex == 0) {
return "Position";
} else if (columnIndex-1 < _columns) {
return Integer.toHexString(columnIndex-1).toUpperCase();
} else {
return "String";
}
}
public int getColumnCount() {
return _columns + 2;
}
public int getRowCount() {
if (_data == null || _data.length == 0) {
return 0;
}
if (_data.length % _columns == 0) {
return (_data.length / _columns);
} else {
return (_data.length / _columns) + 1;
}
}
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return pad(Integer.toHexString(rowIndex * _columns).toUpperCase(), '0', 8);
} else if (columnIndex-1 < _columns) {
int position = rowIndex * _columns + columnIndex-1;
if (position < _data.length) {
int i = _data[position];
if (i<0) { i = i + 256; }
return pad(Integer.toString(i, 16).toUpperCase(),'0',2);
} else {
return "";
}
} else {
int start = rowIndex * _columns;
StringBuffer buff = new StringBuffer();
for (int i=0; i < _columns; i++) {
int pos = start + i;
if (pos >= _data.length) { return buff.toString(); }
if (_data[pos] < 32 || _data[pos] > 126) {
buff.append(".");
} else {
buff.append((char) _data[pos]);
}
}
return buff.toString();
}
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
if (columnIndex == 0 || columnIndex>_columns) {
return false;
}
int position = rowIndex * _columns + columnIndex-1;
if (position < _data.length) {
return _editable;
}
return false;
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
int position = rowIndex * _columns + columnIndex-1;
if (position >= _data.length) {
System.out.println("Out of range");
return;
}
if (aValue instanceof String) {
try {
String s = (String) aValue;
_data[position] = new Integer(Integer.parseInt(s.trim(), 16)).byteValue();
fireTableCellUpdated(rowIndex, _columns + 1);
_modified = true;
} catch (NumberFormatException nfe) {
System.out.println("Number format error : " + nfe);
}
} else {
System.out.println("Value is a " + aValue.getClass().getName());
}
}
private String pad(String initial, char padchar, int length) {
if (initial.length() >= length) {
return initial;
}
StringBuffer buff = new StringBuffer(length);
for (int i=0; i<length - initial.length(); i++) {
buff.append(padchar);
}
buff.append(initial);
return buff.toString();
}
}
}