/**
* Created : Jun 7, 2012
*
* @author pquiring
*/
import java.awt.*;
import java.awt.datatransfer.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.table.*;
import javaforce.*;
public class MainPanel extends javax.swing.JPanel {
/**
* Creates new form MainPanel
*/
public MainPanel() {
initComponents();
This = this;
safe.setUseUniqueNames(true);
safe.setUseAttributeNameForName(true);
safeGroups.setUseUniqueNames(true);
safeGroups.setUseAttributeNameForName(true);
safe.setRoot("group", "name=\"Passwords\"", "");
loadConfig();
copyGroups();
groups.setModel(safeGroups.getTreeModel());
table = (DefaultTableModel)entries.getModel();
initDND();
}
/**
* 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.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jToolBar1 = new javax.swing.JToolBar();
newSafe = new javax.swing.JButton();
openSafe = new javax.swing.JButton();
closeSafe = new javax.swing.JButton();
saveSafe = new javax.swing.JButton();
jSeparator1 = new javax.swing.JToolBar.Separator();
settings = new javax.swing.JButton();
jSeparator2 = new javax.swing.JToolBar.Separator();
importFile = new javax.swing.JButton();
exportFile = new javax.swing.JButton();
jSeparator3 = new javax.swing.JToolBar.Separator();
progress = new javax.swing.JProgressBar();
jSeparator4 = new javax.swing.JToolBar.Separator();
jSplitPane1 = new javax.swing.JSplitPane();
jPanel1 = new javax.swing.JPanel();
jToolBar2 = new javax.swing.JToolBar();
addGroup = new javax.swing.JButton();
deleteGroup = new javax.swing.JButton();
renameGroup = new javax.swing.JButton();
jScrollPane3 = new javax.swing.JScrollPane();
groups = new javax.swing.JTree();
jPanel2 = new javax.swing.JPanel();
jToolBar3 = new javax.swing.JToolBar();
addEntry = new javax.swing.JButton();
deleteEntry = new javax.swing.JButton();
editEntry = new javax.swing.JButton();
copyUsername = new javax.swing.JButton();
copyPassword = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
entries = new javax.swing.JTable();
jToolBar1.setFloatable(false);
jToolBar1.setRollover(true);
newSafe.setText("New");
newSafe.setFocusable(false);
newSafe.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
newSafe.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
newSafe.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
newSafeActionPerformed(evt);
}
});
jToolBar1.add(newSafe);
openSafe.setText("Open");
openSafe.setFocusable(false);
openSafe.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
openSafe.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
openSafe.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
openSafeActionPerformed(evt);
}
});
jToolBar1.add(openSafe);
closeSafe.setText("Close");
closeSafe.setFocusable(false);
closeSafe.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
closeSafe.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
closeSafe.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
closeSafeActionPerformed(evt);
}
});
jToolBar1.add(closeSafe);
saveSafe.setText("Save");
saveSafe.setFocusable(false);
saveSafe.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
saveSafe.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
saveSafe.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
saveSafeActionPerformed(evt);
}
});
jToolBar1.add(saveSafe);
jToolBar1.add(jSeparator1);
settings.setText("Settings");
settings.setFocusable(false);
settings.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
settings.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
settings.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
settingsActionPerformed(evt);
}
});
jToolBar1.add(settings);
jToolBar1.add(jSeparator2);
importFile.setText("Import");
importFile.setFocusable(false);
importFile.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
importFile.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
importFile.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
importFileActionPerformed(evt);
}
});
jToolBar1.add(importFile);
exportFile.setText("Export");
exportFile.setFocusable(false);
exportFile.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
exportFile.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
exportFile.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
exportFileActionPerformed(evt);
}
});
jToolBar1.add(exportFile);
jToolBar1.add(jSeparator3);
jToolBar1.add(progress);
jToolBar1.add(jSeparator4);
jSplitPane1.setDividerLocation(250);
jSplitPane1.setResizeWeight(0.3);
jToolBar2.setFloatable(false);
jToolBar2.setRollover(true);
addGroup.setText("Add");
addGroup.setFocusable(false);
addGroup.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
addGroup.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
addGroup.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
addGroupActionPerformed(evt);
}
});
jToolBar2.add(addGroup);
deleteGroup.setText("Delete");
deleteGroup.setFocusable(false);
deleteGroup.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
deleteGroup.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
deleteGroup.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
deleteGroupActionPerformed(evt);
}
});
jToolBar2.add(deleteGroup);
renameGroup.setText("Rename");
renameGroup.setFocusable(false);
renameGroup.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
renameGroup.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
renameGroup.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
renameGroupActionPerformed(evt);
}
});
jToolBar2.add(renameGroup);
javax.swing.tree.DefaultMutableTreeNode treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("root");
groups.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1));
groups.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
groupsValueChanged(evt);
}
});
jScrollPane3.setViewportView(groups);
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jToolBar2, javax.swing.GroupLayout.DEFAULT_SIZE, 249, Short.MAX_VALUE)
.addComponent(jScrollPane3)
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jToolBar2, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 481, Short.MAX_VALUE))
);
jSplitPane1.setLeftComponent(jPanel1);
jToolBar3.setFloatable(false);
jToolBar3.setRollover(true);
addEntry.setText("Add");
addEntry.setFocusable(false);
addEntry.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
addEntry.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
addEntry.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
addEntryActionPerformed(evt);
}
});
jToolBar3.add(addEntry);
deleteEntry.setText("Delete");
deleteEntry.setFocusable(false);
deleteEntry.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
deleteEntry.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
deleteEntry.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
deleteEntryActionPerformed(evt);
}
});
jToolBar3.add(deleteEntry);
editEntry.setText("Edit");
editEntry.setFocusable(false);
editEntry.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
editEntry.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
editEntry.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
editEntryActionPerformed(evt);
}
});
jToolBar3.add(editEntry);
copyUsername.setText("Copy Username");
copyUsername.setToolTipText("Ctrl+Z");
copyUsername.setFocusable(false);
copyUsername.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
copyUsername.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
copyUsername.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
copyUsernameActionPerformed(evt);
}
});
jToolBar3.add(copyUsername);
copyPassword.setText("Copy Password");
copyPassword.setToolTipText("Ctrl+C");
copyPassword.setFocusable(false);
copyPassword.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
copyPassword.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
copyPassword.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
copyPasswordActionPerformed(evt);
}
});
jToolBar3.add(copyPassword);
entries.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
},
new String [] {
"Title", "User Name", "URL"
}
) {
Class[] types = new Class [] {
java.lang.String.class, java.lang.String.class, java.lang.String.class
};
boolean[] canEdit = new boolean [] {
false, false, false
};
public Class getColumnClass(int columnIndex) {
return types [columnIndex];
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return canEdit [columnIndex];
}
});
entries.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
entriesMouseClicked(evt);
}
});
entries.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent evt) {
entriesKeyPressed(evt);
}
public void keyTyped(java.awt.event.KeyEvent evt) {
entriesKeyTyped(evt);
}
});
jScrollPane1.setViewportView(entries);
javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
jPanel2.setLayout(jPanel2Layout);
jPanel2Layout.setHorizontalGroup(
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jToolBar3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 375, Short.MAX_VALUE)
);
jPanel2Layout.setVerticalGroup(
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel2Layout.createSequentialGroup()
.addComponent(jToolBar3, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 481, Short.MAX_VALUE))
);
jSplitPane1.setRightComponent(jPanel2);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jSplitPane1)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSplitPane1))
);
}// </editor-fold>//GEN-END:initComponents
private void newSafeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newSafeActionPerformed
newSafe();
}//GEN-LAST:event_newSafeActionPerformed
private void openSafeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openSafeActionPerformed
openSafe();
}//GEN-LAST:event_openSafeActionPerformed
private void closeSafeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeSafeActionPerformed
closeSafe();
}//GEN-LAST:event_closeSafeActionPerformed
private void saveSafeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveSafeActionPerformed
saveSafe();
}//GEN-LAST:event_saveSafeActionPerformed
private void settingsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_settingsActionPerformed
editConfig();
}//GEN-LAST:event_settingsActionPerformed
private void addGroupActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addGroupActionPerformed
addGroup();
}//GEN-LAST:event_addGroupActionPerformed
private void deleteGroupActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteGroupActionPerformed
deleteGroup();
}//GEN-LAST:event_deleteGroupActionPerformed
private void renameGroupActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_renameGroupActionPerformed
renameGroup();
}//GEN-LAST:event_renameGroupActionPerformed
private void addEntryActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addEntryActionPerformed
addEntry();
}//GEN-LAST:event_addEntryActionPerformed
private void deleteEntryActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteEntryActionPerformed
deleteEntry();
}//GEN-LAST:event_deleteEntryActionPerformed
private void editEntryActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editEntryActionPerformed
editEntry();
}//GEN-LAST:event_editEntryActionPerformed
private void importFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importFileActionPerformed
importFile();
}//GEN-LAST:event_importFileActionPerformed
private void exportFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportFileActionPerformed
exportFile();
}//GEN-LAST:event_exportFileActionPerformed
private void groupsValueChanged(javax.swing.event.TreeSelectionEvent evt) {//GEN-FIRST:event_groupsValueChanged
listEntries();
}//GEN-LAST:event_groupsValueChanged
private void entriesMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_entriesMouseClicked
if (evt.getClickCount() == 2) editEntry();
}//GEN-LAST:event_entriesMouseClicked
private void entriesKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_entriesKeyPressed
int mod = evt.getModifiers();
int kc = evt.getKeyChar();
int cc = evt.getKeyCode();
// JFLog.log("key:" + mod + "," + kc + "," + cc);
if (kc == '\n' && mod == 0) {
evt.consume();
editEntry();
return;
}
if (mod != java.awt.event.KeyEvent.CTRL_MASK) return;
switch (cc) {
case 'C': copyPassword(); evt.consume(); break;
case 'Z': copyUsername(); evt.consume(); break;
}
}//GEN-LAST:event_entriesKeyPressed
private void copyPasswordActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyPasswordActionPerformed
copyPassword();
}//GEN-LAST:event_copyPasswordActionPerformed
private void entriesKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_entriesKeyTyped
char ch = evt.getKeyChar();
int len = table.getRowCount();
int idx = entries.getSelectedRow();
if (idx < 0) idx = 0;
int start = idx;
do {
idx++;
if (idx >= len) idx = 0;
String title = (String)table.getValueAt(idx, 0);
if (title.toLowerCase().charAt(0) == ch) {
entries.changeSelection(idx, idx, false, false);
return;
}
} while (idx != start);
}//GEN-LAST:event_entriesKeyTyped
private void copyUsernameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyUsernameActionPerformed
copyUsername();
}//GEN-LAST:event_copyUsernameActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton addEntry;
private javax.swing.JButton addGroup;
private javax.swing.JButton closeSafe;
private javax.swing.JButton copyPassword;
private javax.swing.JButton copyUsername;
private javax.swing.JButton deleteEntry;
private javax.swing.JButton deleteGroup;
private javax.swing.JButton editEntry;
private javax.swing.JTable entries;
private javax.swing.JButton exportFile;
private javax.swing.JTree groups;
private javax.swing.JButton importFile;
private javax.swing.JPanel jPanel1;
private javax.swing.JPanel jPanel2;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JScrollPane jScrollPane3;
private javax.swing.JToolBar.Separator jSeparator1;
private javax.swing.JToolBar.Separator jSeparator2;
private javax.swing.JToolBar.Separator jSeparator3;
private javax.swing.JToolBar.Separator jSeparator4;
private javax.swing.JSplitPane jSplitPane1;
private javax.swing.JToolBar jToolBar1;
private javax.swing.JToolBar jToolBar2;
private javax.swing.JToolBar jToolBar3;
private javax.swing.JButton newSafe;
private javax.swing.JButton openSafe;
private javax.swing.JProgressBar progress;
private javax.swing.JButton renameGroup;
private javax.swing.JButton saveSafe;
private javax.swing.JButton settings;
// End of variables declaration//GEN-END:variables
public static MainPanel This;
public static Config config;
private String configFolder = JF.getUserPath();
private String configFile = "/.jfpasswords.xml";
private XML safe = new XML();
private XML safeGroups = new XML(); //a copy with just groups
protected static String password;
private DefaultTableModel table;
private java.util.Timer timer;
private Object timerLock = new Object();
private int timerCount;
public boolean dirty = false;
public long timestamp = -1;
public boolean loaded = false;
public static class Config {
public String safe = "";
public int passwordGeneratorLength = 12;
public boolean passwordGeneratorSymbols = true;
public boolean passwordGeneratorAmbiguous = true;
public boolean reAuthOnShow = true;
public boolean bWindowMax = false;
public int WindowXSize = -1, WindowYSize = -1;
public int WindowXPos = 0, WindowYPos = 0;
}
private void loadConfig() {
config = new Config();
try {
XML xml = new XML();
FileInputStream fis = new FileInputStream(configFolder + configFile);
xml.read(fis);
xml.writeClass(config);
} catch (FileNotFoundException e1) {
config = new Config();
} catch (Exception e2) {
JFLog.log(e2);
config = new Config();
}
}
private void saveConfig() {
try {
XML xml = new XML();
FileOutputStream fos = new FileOutputStream(configFolder + configFile);
xml.readClass("jpassword", config);
xml.write(fos);
fos.close();
} catch (Exception e) {
JFLog.log(e);
}
}
private String getSafeName() {
String name = config.safe;
int idx = name.lastIndexOf("/");
if (idx == -1) idx = 0; else idx++;
return name.substring(idx, name.length() - 5);
}
public boolean loadSafe(boolean reopen) {
if (!reopen) {
password = GetPassword.getPassword(null);
if (password == null) return false;
} else {
if (password == null) return false;
}
try {
safe.deleteAll();
File file = new File(config.safe);
timestamp = file.lastModified();
FileInputStream fis = new FileInputStream(config.safe);
byte encrypted[] = JF.readAll(fis);
fis.close();
Data.setPassword(password);
byte decrypted[] = Data.decrypt(encrypted);
if (decrypted == null) {
throw new Exception("decrypt failed");
}
safe.read(new ByteArrayInputStream(decrypted));
safe.setRoot("group", "name=\"" + getSafeName() + "\"", "");
copyGroups();
dirty = false;
loaded = true;
return true;
} catch (Exception e) {
JF.showError("Error", "Wrong Password");
safe.deleteAll();
password = null;
timestamp = -1;
dirty = false;
JFLog.log(e);
return false;
}
}
private void copyGroups() {
safeGroups.deleteAll();
safeGroups.setRoot("group", "name=\"" + safe.root.getArg("name") + "\"", "");
copyGroups(safe.root, safeGroups.root);
}
private void copyGroups(XML.XMLTag src, XML.XMLTag dst) {
int cnt = src.getChildCount();
groups.makeVisible(new TreePath(dst.getPath()));
for(int a=0;a<cnt;a++) {
XML.XMLTag tag = src.getChildAt(a);
if (tag.name.equals("group")) {
XML.XMLTag newtag = safeGroups.addTag(dst, "group", "name=\"" + tag.getArg("name") + "\"", "");
copyGroups(tag, newtag);
continue;
}
}
}
public boolean saveSafe() {
if (config.safe.length() == 0) {
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
chooser.setMultiSelectionEnabled(false);
chooser.setCurrentDirectory(new File(JF.getUserPath() + "/Documents"));
javax.swing.filechooser.FileFilter ffSafe = new javax.swing.filechooser.FileFilter() {
public boolean accept(File file) {
if (file.isDirectory()) return true;
if (file.getName().endsWith(".safe")) return true;
return false;
}
public String getDescription() {
return "Safe (*.safe)";
}
};
chooser.addChoosableFileFilter(ffSafe);
chooser.setFileFilter(ffSafe);
if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) return false;
config.safe = chooser.getSelectedFile().getAbsolutePath();
if (!config.safe.endsWith(".safe")) config.safe += ".safe";
safe.setRoot("group", "name=\"" + getSafeName() + "\"", "");
copyGroups();
}
if (password == null) {
String pass1 = GetPassword.getPassword(null, "Create Password");
String pass2 = GetPassword.getPassword(null, "Confirm Password");
if (!pass1.equals(pass2)) {
JF.showError("Error", "Passwords do not match");
return false;
}
password = pass1;
}
try {
FileOutputStream fos = new FileOutputStream(config.safe);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
safe.write(baos);
Data.setPassword(password);
byte encrypted[] = Data.encrypt(baos.toByteArray());
fos.write(encrypted);
fos.close();
File file = new File(config.safe);
timestamp = file.lastModified();
dirty = false;
saveConfig();
loaded = true;
return true;
} catch (Exception e) {
JF.showError("Error", "Failed to save safe!\nError:" + e.toString());
JFLog.log(e);
return false;
}
}
private void openSafe() {
if (dirty) {
switch (JF.showConfirm3("Confirm", "Save first?")) {
case JF.YES: saveSafe(); break;
case JF.NO: break;
case JF.CANCEL: return;
}
}
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
chooser.setMultiSelectionEnabled(false);
chooser.setCurrentDirectory(new File(JF.getUserPath() + "/Documents"));
javax.swing.filechooser.FileFilter ffSafe = new javax.swing.filechooser.FileFilter() {
public boolean accept(File file) {
if (file.isDirectory()) return true;
if (file.getName().endsWith(".safe")) return true;
return false;
}
public String getDescription() {
return "Safe (*.safe)";
}
};
chooser.addChoosableFileFilter(ffSafe);
chooser.setFileFilter(ffSafe);
if (chooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) return;
config.safe = chooser.getSelectedFile().getAbsolutePath();
saveConfig();
loadSafe(false);
}
private void newSafe() {
if (dirty) {
switch (JF.showConfirm3("Confirm", "Save first?")) {
case JF.YES: saveSafe(); break;
case JF.NO: break;
case JF.CANCEL: return;
}
}
safe.deleteAll();
safe.setRoot("group", "name=\"Passwords\"", "");
config.safe = "";
password = null;
timestamp = -1;
saveConfig();
copyGroups();
loaded = false;
}
private void closeSafe() {
newSafe();
}
private void editConfig() {
EditConfig dialog = new EditConfig(null, true);
dialog.setVisible(true);
if (dialog.accepted) saveConfig();
}
private void addGroup() {
String name = JF.getString("Enter Group Name:", "");
if (name == null) return;
TreePath path = groups.getSelectionPath();
XML.XMLTag selTag;
if (path == null) {
selTag = safe.root;
} else {
selTag = safe.getTag(path);
}
safe.addTag(selTag, "group", "name=\"" + name + "\"", "");
copyGroups();
dirty = true;
}
private void deleteGroup() {
TreePath path = groups.getSelectionPath();
XML.XMLTag selTag;
if (path == null) return;
selTag = safe.getTag(path);
if (!JF.showConfirm("Confirm", "Are you sure?")) return;
safe.deleteTag(selTag);
copyGroups();
dirty = true;
}
private void renameGroup() {
TreePath path = groups.getSelectionPath();
XML.XMLTag selTag;
if (path == null) return;
selTag = safe.getTag(path);
String newName = JF.getString("Enter new name", selTag.getArg("name"));
if ((newName == null) || (newName.length() == 0)) return;
selTag.setArg("name", newName);
copyGroups();
dirty = true;
}
private static class Entry implements Comparable {
public String title, username, url;
public int compareTo(Object o) {
Entry e = (Entry)o;
return title.compareTo(e.title);
}
}
private void listEntries() {
table.setRowCount(0);
TreePath path = groups.getSelectionPath();
XML.XMLTag selTag;
if (path == null) {
selTag = safe.root;
} else {
selTag = safe.getTag(path);
}
if (selTag == null) return;
int cnt = selTag.getChildCount();
ArrayList<Entry> alist = new ArrayList<Entry>();
for(int a=0;a<cnt;a++) {
XML.XMLTag child = selTag.getChildAt(a);
if (!child.name.equals("entry")) continue;
int cnt2 = child.getChildCount();
String title = child.getArg("name"), username = "", pass = "", url = "", notes = "";
for(int b=0;b<cnt2;b++) {
XML.XMLTag f = child.getChildAt(b);
if (f.name.equals("username")) {
username = f.getContent();
continue;
}
if (f.name.equals("password")) {
pass = f.getContent();
continue;
}
if (f.name.equals("url")) {
url = f.getContent();
continue;
}
if (f.name.equals("notes")) {
notes = f.getContent();
continue;
}
}
Entry e = new Entry();
e.title = title;
e.username = username;
e.url = url;
alist.add(e);
}
Entry[] elist = alist.toArray(new Entry[0]);
Arrays.sort(elist);
for(int a=0;a<elist.length;a++) {
Entry e = elist[a];
table.addRow(new Object[] {e.title, e.username, e.url});
}
}
private void addEntry() {
EditEntry dialog = new EditEntry(null, true, null, table, -1);
dialog.setVisible(true);
if (!dialog.accepted) return;
TreePath path = groups.getSelectionPath();
XML.XMLTag selTag;
if (path == null) {
selTag = safe.root;
} else {
selTag = safe.getTag(path);
}
XML.XMLTag entry = safe.addTag(selTag, "entry", "", "");
dialog.saveTo(safe, entry);
listEntries();
dirty = true;
}
private void deleteEntry() {
TreePath path = groups.getSelectionPath();
if (path == null) return;
XML.XMLTag selTag;
selTag = safe.getTag(path);
int row = entries.getSelectedRow();
if (row == -1) return;
String title = (String)entries.getValueAt(row, 0);
int cnt = selTag.getChildCount();
XML.XMLTag child = null;
for(int a=0;a<cnt;a++) {
child = selTag.getChildAt(a);
if (child.name.equals("entry") && child.getArg("name").equals(title)) break;
child = null;
}
if (child == null) return;
if (!JF.showConfirm("Confirm", "Are you sure?")) return;
safe.deleteTag(child);
listEntries();
dirty = true;
}
private void editEntry() {
TreePath path = groups.getSelectionPath();
XML.XMLTag selTag;
if (path == null) return;
selTag = safe.getTag(path);
int row = entries.getSelectedRow();
if (row == -1) return;
String title = (String)entries.getValueAt(row, 0);
int cnt = selTag.getChildCount();
boolean ok = false;
XML.XMLTag child = null;
for(int a=0;a<cnt;a++) {
child = selTag.getChildAt(a);
if (child.getArg("name").equals(title)) {
ok = true;
break;
}
}
if (!ok) return;
EditEntry dialog = new EditEntry(null, true, child, table, row);
dialog.setVisible(true);
if (!dialog.accepted) return;
dialog.saveTo(safe, child);
listEntries();
dirty = true;
}
private void importFile() {
try {
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
chooser.setMultiSelectionEnabled(false);
chooser.setCurrentDirectory(new File(JF.getUserPath() + "/Documents"));
javax.swing.filechooser.FileFilter ffKeePass = new javax.swing.filechooser.FileFilter() {
public boolean accept(File file) {
if (file.isDirectory()) return true;
if (file.getName().endsWith(".xml")) return true;
return false;
}
public String getDescription() {
return "KeePass 2.x (*.xml)";
}
};
chooser.addChoosableFileFilter(ffKeePass);
javax.swing.filechooser.FileFilter ffUnSafe = new javax.swing.filechooser.FileFilter() {
public boolean accept(File file) {
if (file.isDirectory()) return true;
if (file.getName().endsWith(".unsafe")) return true;
return false;
}
public String getDescription() {
return "jPasswords (*.unsafe)";
}
};
chooser.addChoosableFileFilter(ffKeePass);
chooser.setFileFilter(ffKeePass);
if (chooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) return;
File file = chooser.getSelectedFile();
if (file.getName().endsWith(".xml")) {
importFileKeePass2(file);
return;
}
if (file.getName().endsWith(".unsafe")) {
importFileUnSafe(file);
return;
}
} catch (Exception e) {
JFLog.log(e);
JF.showError("Error", "Import failed");
}
}
private void importFileKeePass2(File file) {
try {
FileInputStream fis = new FileInputStream(file);
XML xml = new XML();
xml.read(fis);
fis.close();
//find the root tag
XML.XMLTag root = null;
int cnt=xml.root.getChildCount();
for(int a=0;a<cnt;a++) {
XML.XMLTag child = xml.root.getChildAt(a);
if (child.name.equals("Root")) {
root = child;
break;
}
}
if (root == null) throw new Exception("Root tag not found");
XML.XMLTag dst = safe.root;
importGroup(root, dst);
copyGroups();
} catch (Exception e) {
JFLog.log(e);
JF.showError("Error", "Import failed");
}
}
private void importGroup(XML.XMLTag src, XML.XMLTag dst) {
int cnt = src.getChildCount();
for(int a=0;a<cnt;a++) {
XML.XMLTag srcChild = src.getChildAt(a);
if (srcChild.name.equals("Group")) {
XML.XMLTag dstChild = safe.addTag(dst, "group", "", "");
importGroup(srcChild, dstChild);
continue;
}
if (srcChild.name.equals("Name")) {
dst.setArg("name", srcChild.getContent());
continue;
}
if (srcChild.name.equals("Entry")) {
XML.XMLTag dstChild = safe.addTag(dst, "entry", "", "");
importEntry(srcChild, dstChild);
continue;
}
}
}
private void importEntry(XML.XMLTag src, XML.XMLTag dst) {
int cnt = src.getChildCount();
for(int a=0;a<cnt;a++) {
XML.XMLTag child = src.getChildAt(a);
if (child.name.equals("String")) {
String Key = "", Value = "";
int cntChild = child.getChildCount();
for(int b=0;b<cntChild;b++) {
XML.XMLTag child2 = child.getChildAt(b);
if (child2.name.equals("Key")) {
Key = child2.getContent();
continue;
}
if (child2.name.equals("Value")) {
Value = child2.getContent();
continue;
}
}
if (Key.equals("Title")) {
dst.setArg("name", Value);
continue;
}
if (Key.equals("UserName")) {
safe.addTag(dst, "username", "", Value);
continue;
}
if (Key.equals("Password")) {
safe.addTag(dst, "password", "", Value);
continue;
}
if (Key.equals("URL")) {
safe.addTag(dst, "url", "", Value);
continue;
}
if (Key.equals("Notes")) {
safe.addTag(dst, "notes", "", Value);
continue;
}
}
}
}
private void importFileUnSafe(File file) {
try {
FileInputStream fis = new FileInputStream(file);
safe.read(fis);
fis.close();
copyGroups();
} catch (Exception e) {
JFLog.log(e);
JF.showError("Error", "Import failed");
}
}
private void exportFile() {
try {
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
chooser.setMultiSelectionEnabled(false);
chooser.setCurrentDirectory(new File(JF.getUserPath() + "/Documents"));
javax.swing.filechooser.FileFilter ffunSafe = new javax.swing.filechooser.FileFilter() {
public boolean accept(File file) {
if (file.isDirectory()) return true;
if (file.getName().endsWith(".unsafe")) return true;
return false;
}
public String getDescription() {
return "jPasswords (*.unsafe)";
}
};
chooser.addChoosableFileFilter(ffunSafe);
chooser.setFileFilter(ffunSafe);
if (chooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) return;
String filename = chooser.getSelectedFile().getAbsolutePath();
if (!filename.endsWith(".unsafe")) filename += ".unsafe";
FileOutputStream fos = new FileOutputStream(filename);
safe.write(fos);
fos.close();
} catch (Exception e) {
JFLog.log(e);
JF.showError("Error", "Export failed");
}
}
private void copyPassword() {
String pass = null;
TreePath path = groups.getSelectionPath();
XML.XMLTag selTag;
if (path == null) return;
selTag = safe.getTag(path);
int row = entries.getSelectedRow();
if (row == -1) return;
String title = (String)entries.getValueAt(row, 0);
int cnt = selTag.getChildCount();
boolean ok = false;
XML.XMLTag child = null;
for(int a=0;a<cnt;a++) {
child = selTag.getChildAt(a);
if (child.getArg("name").equals(title)) {
ok = true;
break;
}
}
if (!ok) return;
int cntChild = child.getChildCount();
XML.XMLTag f;
for(int a=0;a<cntChild;a++) {
f = child.getChildAt(a);
if (f.name.equals("password")) {
pass = f.getContent();
break;
}
}
if (pass == null) return;
JFLog.log("Copy password to clipboard");
StringSelection ss = new StringSelection(pass);
Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
if (cb == null) return;
cb.setContents(ss, ss);
startTimer();
}
private void copyUsername() {
String username = null;
TreePath path = groups.getSelectionPath();
XML.XMLTag selTag;
if (path == null) return;
selTag = safe.getTag(path);
int row = entries.getSelectedRow();
if (row == -1) return;
String title = (String)entries.getValueAt(row, 0);
int cnt = selTag.getChildCount();
boolean ok = false;
XML.XMLTag child = null;
for(int a=0;a<cnt;a++) {
child = selTag.getChildAt(a);
if (child.getArg("name").equals(title)) {
ok = true;
break;
}
}
if (!ok) return;
int cntChild = child.getChildCount();
XML.XMLTag f;
for(int a=0;a<cntChild;a++) {
f = child.getChildAt(a);
if (f.name.equals("username")) {
username = f.getContent();
break;
}
}
if (username == null) return;
JFLog.log("Copy username to clipboard");
StringSelection ss = new StringSelection(username);
Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
if (cb == null) return;
cb.setContents(ss, ss);
startTimer();
}
public void startTimer() {
synchronized(timerLock) {
timerCount = 100;
if (timer != null) return;
timer = new java.util.Timer();
timer.schedule(new java.util.TimerTask() {
public void run() {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run () {
synchronized(timerLock) {
timerCount -= 1;
progress.setValue(timerCount);
if (timerCount == 0) {
timer.cancel();
timer = null;
JFLog.log("clearing password");
StringSelection ss = new StringSelection("");
Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
if (cb == null) return;
cb.setContents(ss, ss);
}
}
}
});
}
}, 100, 100);
}
}
/** Checks if password safe file has changed, and reload if so. */
public void checkTimestamp() {
if (timestamp == -1) return;
File file = new File(config.safe);
if (!file.exists()) return;
long newtimestamp = file.lastModified();
if (newtimestamp != timestamp) {
if (dirty) {
if (!JF.showConfirm("Reload", "Database file has changed with unsaved changes!\nReload?")) {
return;
}
} else {
if (!JF.showConfirm("Reload", "Database file has changed!\nReload?")) {
return;
}
}
loadSafe(true);
}
}
private class EntryFlavor extends DataFlavor {
public EntryFlavor() throws ClassNotFoundException {
super("text/custom1");
}
public XML.XMLTag group;
public XML.XMLTag entries[];
}
private EntryFlavor ef;
private class EntryTransfer implements Transferable {
public EntryFlavor data;
public EntryTransfer() {
try {
data = new EntryFlavor();
} catch (Exception e) {
JFLog.log(e);
}
}
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] {ef};
}
public boolean isDataFlavorSupported(DataFlavor flavor) {
return (flavor instanceof EntryFlavor);
}
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
// if (!(flavor instanceof EntryFlavor)) return null;
return data;
}
}
private class GroupFlavor extends DataFlavor {
public GroupFlavor() throws ClassNotFoundException {
super("text/custom2");
}
public XML.XMLTag group;
}
private GroupFlavor gf;
private class GroupTransfer implements Transferable {
public GroupFlavor data;
public GroupTransfer() {
try {
data = new GroupFlavor();
} catch (Exception e) {
JFLog.log(e);
}
}
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] {gf};
}
public boolean isDataFlavorSupported(DataFlavor flavor) {
return (flavor instanceof GroupFlavor);
}
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
// if (!(flavor instanceof EntryFlavor)) return null;
return data;
}
}
private void initDND() {
try {
ef = new EntryFlavor();
gf = new GroupFlavor();
} catch (Exception e) {
JFLog.log(e);
}
//right side (entries)
entries.setDragEnabled(true);
entries.setTransferHandler(
new TransferHandler() {
public boolean canImport(TransferHandler.TransferSupport info) {
// JFLog.log("entries:canImport:" + info);
return false;
}
public boolean importData(TransferHandler.TransferSupport info) {
// JFLog.log("entries:importData:" + info);
return false;
}
public int getSourceActions(JComponent c) {
// JFLog.log("entries:getSourceActions");
return MOVE;
}
protected Transferable createTransferable(JComponent c) {
// JFLog.log("entries:createTransferable");
EntryTransfer t = new EntryTransfer();
TreePath path = groups.getSelectionPath();
XML.XMLTag group;
if (path == null) return null;
group = safe.getTag(path);
int rows[] = entries.getSelectedRows();
t.data.entries = new XML.XMLTag[rows.length];
for(int row=0;row<rows.length;row++) {
String title = (String)entries.getValueAt(rows[row], 0);
int cnt = group.getChildCount();
boolean ok = false;
XML.XMLTag child = null;
for(int a=0;a<cnt;a++) {
child = group.getChildAt(a);
if (child.getArg("name").equals(title)) {
ok = true;
break;
}
}
if (!ok) return null;
t.data.entries[row] = child;
}
t.data.group = group;
return t;
}
protected void exportDone(JComponent source, Transferable data, int action) {
// JFLog.log("entries:exportDone:" + data);
if (action != MOVE) return;
}
}
);
//left side (groups)
groups.setDragEnabled(true);
// groups.setDropMode(DropMode.USE_SELECTION);
groups.setTransferHandler(
new TransferHandler() {
public boolean canImport(TransferHandler.TransferSupport info) {
// JFLog.log("groups:canImport:" + info);
if (!info.isDataFlavorSupported(ef) && !info.isDataFlavorSupported(gf)) {
// JFLog.log("groups:canImport:false");
return false;
}
try {
Transferable t = info.getTransferable();
// JFLog.log("t=" + t.getTransferData(ef));
} catch (Exception e) {
JFLog.log(e);
}
return true;
}
public boolean importData(TransferHandler.TransferSupport info) {
// JFLog.log("groups:importData=" + info);
if (info.isDataFlavorSupported(ef)) {
try {
EntryFlavor data = (EntryFlavor)info.getTransferable().getTransferData(ef);
TreePath path = groups.getSelectionPath();
XML.XMLTag dest;
if (path == null) return false;
dest = safe.getTag(path);
if (dest == null) return false;
if (dest == data.group) {
JFLog.log("src == dest");
return false;
}
//move data.entries to dest tag
JFLog.log("moving " + data.entries.length + " entries");
JFLog.log(" src=" + data.group);
JFLog.log(" dest=" + dest);
for(int a=0;a<data.entries.length;a++) {
//do not use setParent() it does not update child lists
//instead use add() which updates child lists in src and dest
dest.add(data.entries[a]);
}
listEntries(); //refresh
return true;
} catch (Exception e) {
JFLog.log(e);
}
}
if (info.isDataFlavorSupported(gf)) {
try {
GroupFlavor data = (GroupFlavor)info.getTransferable().getTransferData(gf);
TreePath path = groups.getSelectionPath();
XML.XMLTag dest;
if (path == null) return false;
dest = safe.getTag(path);
if (dest == null) return false;
if (dest == data.group) {
JFLog.log("src == dest");
return false;
}
dest.add(data.group);
copyGroups();
return true;
} catch (Exception e) {
JFLog.log(e);
}
}
return false;
}
public int getSourceActions(JComponent c) {
return MOVE;
}
protected Transferable createTransferable(JComponent c) {
GroupTransfer t = new GroupTransfer();
TreePath path = groups.getSelectionPath();
if (path == null) return null;
t.data.group = safe.getTag(path);
return t;
}
protected void exportDone(JComponent source, Transferable data, int action) {
}
}
);
}
}