/** * 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) { } } ); } }