497 lines
15 KiB
Java
497 lines
15 KiB
Java
/*
|
|
* Copyright (C) 2009-2017 Alistair Neil <info@dazzleships.net>
|
|
*
|
|
* 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.
|
|
*/
|
|
package lib;
|
|
|
|
import java.awt.Desktop;
|
|
import java.awt.FontMetrics;
|
|
import java.awt.Frame;
|
|
import java.awt.event.WindowEvent;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.IOException;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.net.URI;
|
|
import java.net.URISyntaxException;
|
|
import java.security.MessageDigest;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.security.SecureRandom;
|
|
import java.util.ArrayList;
|
|
import java.util.Formatter;
|
|
import java.util.Random;
|
|
import java.util.regex.Pattern;
|
|
import javax.swing.JFileChooser;
|
|
import javax.swing.JFrame;
|
|
import javax.swing.JTable;
|
|
import javax.swing.UIManager;
|
|
import javax.swing.UnsupportedLookAndFeelException;
|
|
import javax.swing.filechooser.FileFilter;
|
|
|
|
/**
|
|
*
|
|
* @author Alistair Neil <info@dazzleships.net>
|
|
*/
|
|
public final class Utilities {
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public Utilities() {
|
|
}
|
|
|
|
/**
|
|
* Covert list to csv
|
|
*
|
|
* @param al
|
|
* @return String of comma separated values
|
|
*/
|
|
public static String getListAsCSV(ArrayList<String> al) {
|
|
return al.toString().replace("[", "").replace("]", "").replace(" ", "");
|
|
}
|
|
|
|
/**
|
|
* Preloads filechooser in background so it will open instantly when
|
|
* requested
|
|
*/
|
|
public static void preloadFileChooser() {
|
|
Thread t = new Thread(new java.lang.Runnable() {
|
|
|
|
@Override
|
|
public void run() {
|
|
JFileChooser fileChooser = new JFileChooser();
|
|
}
|
|
});
|
|
t.start();
|
|
}
|
|
|
|
/**
|
|
* Opens java file chooser dialog, convenience method. Returns filepath if
|
|
* successful and null if not
|
|
*
|
|
* @param parent
|
|
* @param folder
|
|
* @param filter Extension filter
|
|
* @param mode Fileselection mode
|
|
* @return String Filepath
|
|
*/
|
|
public static String openFileChooser(Frame parent, String folder, FileFilter filter, int mode) {
|
|
JFileChooser fileChooser = new JFileChooser();
|
|
fileChooser.setFileSelectionMode(mode);
|
|
fileChooser.setCurrentDirectory(new File(folder));
|
|
if (filter != null) {
|
|
fileChooser.addChoosableFileFilter(filter);
|
|
}
|
|
int intReturn = fileChooser.showOpenDialog(parent);
|
|
if (intReturn == JFileChooser.APPROVE_OPTION) {
|
|
return fileChooser.getSelectedFile().getAbsolutePath();
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for newer appversion
|
|
*
|
|
* @param localversion
|
|
* @param remoteversion
|
|
* @return True if newer
|
|
*/
|
|
public static boolean isNewerVersion(String localversion, String remoteversion) {
|
|
return convertBuildnumToFloat(remoteversion) > convertBuildnumToFloat(localversion);
|
|
}
|
|
|
|
/**
|
|
* Convert an debian build number into a floating point number
|
|
*
|
|
* @param version
|
|
* @return Float version
|
|
*/
|
|
private static float convertBuildnumToFloat(String version) {
|
|
|
|
float result;
|
|
Pattern p;
|
|
String[] parts;
|
|
try {
|
|
if (version.contains("-")) {
|
|
// Handling for old non-debian compatible version
|
|
p = Pattern.compile("-");
|
|
parts = p.split(version);
|
|
} else {
|
|
// Handling for new debian compatible version
|
|
p = Pattern.compile("\\.");
|
|
parts = p.split(version);
|
|
parts[0] = parts[0] + "." + parts[1];
|
|
parts[1] = parts[2];
|
|
parts[2] = "";
|
|
}
|
|
result = Float.parseFloat(parts[0]);
|
|
result += (Float.parseFloat(parts[1]) / 100000);
|
|
} catch (Exception ex) {
|
|
version = version.replaceAll("[^0-9.]", "");
|
|
result = convertVersionToFloat(version);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Convert a windows version number into a floating point number
|
|
*
|
|
* @param version
|
|
* @return Float version
|
|
*/
|
|
private static float convertVersionToFloat(String version) {
|
|
|
|
int c;
|
|
float f;
|
|
version = version.toLowerCase();
|
|
if (version.startsWith("v")) {
|
|
version = version.substring(1);
|
|
}
|
|
try {
|
|
c = version.charAt(version.length() - 1);
|
|
if (c > 96) {
|
|
version = version.substring(0, version.length() - 1);
|
|
Float fraction = (float) (c - 96) / 10000;
|
|
f = Float.parseFloat(version) + fraction;
|
|
} else {
|
|
f = Float.parseFloat(version);
|
|
}
|
|
} catch (Exception ex) {
|
|
f = 0;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
/**
|
|
* Open the desktop default file editor
|
|
*
|
|
* @param path Path to file
|
|
*/
|
|
public static void editFile(String path) {
|
|
if (OSFunction.isLinux()) {
|
|
OSFunction.launchProcess("xdg-open", path);
|
|
return;
|
|
}
|
|
if (OSFunction.isWindows()) {
|
|
OSFunction.launchProcess("notepad", path);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Launch the desktops default file handling program
|
|
*
|
|
* @param strUrl
|
|
* @return Returns true if successful
|
|
*/
|
|
public static boolean openFileExternally(final String strUrl) {
|
|
String url = strUrl.replace("\\", "/");
|
|
try {
|
|
if (OSFunction.isLinux()) {
|
|
OSFunction.launchProcess("xdg-open", url);
|
|
} else {
|
|
if (Desktop.isDesktopSupported()) {
|
|
Desktop deskSupport = Desktop.getDesktop();
|
|
if (url.startsWith("http") || url.startsWith("ftp")) {
|
|
deskSupport.browse(new URI(url));
|
|
} else {
|
|
deskSupport.open(new File(url));
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
} catch (URISyntaxException | IOException ex) {
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Initialises the UI Look and Feel
|
|
*
|
|
* @param theme
|
|
*/
|
|
public static void loadUIStyle(String theme) {
|
|
|
|
try {
|
|
if (theme == null) {
|
|
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
|
|
return;
|
|
}
|
|
|
|
if (theme.contentEquals("Metal")) {
|
|
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
|
|
return;
|
|
}
|
|
|
|
if (theme.contentEquals("Nimbus")) {
|
|
// Switch to Nimbus theme
|
|
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
|
|
return;
|
|
}
|
|
|
|
if (theme.contentEquals("System")) {
|
|
try {
|
|
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
|
|
return;
|
|
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
|
|
}
|
|
try {
|
|
UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
|
|
return;
|
|
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
|
|
}
|
|
}
|
|
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
|
|
}
|
|
|
|
// If everything else fails use default cross platform metal
|
|
try {
|
|
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
|
|
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adjust specified column width based on the width of a test string
|
|
*
|
|
* @param table
|
|
* @param test
|
|
*/
|
|
public static void adjustTableColumnWidth(JTable table, String... test) {
|
|
FontMetrics ourFontMetrics = table.getFontMetrics(table.getFont());
|
|
int col = 0;
|
|
for (String s : test) {
|
|
table.getColumn(table.getColumnName(col++)).setPreferredWidth(ourFontMetrics.stringWidth(s));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate secret key.
|
|
*
|
|
* @param length
|
|
* @return String key
|
|
*/
|
|
public static String generateSecretKey(int length) {
|
|
StringBuilder buffer = new StringBuilder();
|
|
Random random = new Random();
|
|
String chars = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
int x;
|
|
try {
|
|
for (int i = 0; i < length; i++) {
|
|
x = random.nextInt(chars.length() - 1);
|
|
buffer.append(chars.substring(x, x + 1));
|
|
}
|
|
} catch (Exception ex) {
|
|
}
|
|
return buffer.toString();
|
|
}
|
|
|
|
public static String getTorHashPassword(String secret) {
|
|
byte[] key = S2KRFC2440(secret);
|
|
return "16:" + byteToHex(key);
|
|
}
|
|
|
|
/**
|
|
* Secret to key, Algorithm info taken from
|
|
* http://sunsite.icm.edu.pl/gnupg/rfc2440-3.html
|
|
*
|
|
* @param secret
|
|
* @return hashed key for the given secret
|
|
*/
|
|
public static byte[] S2KRFC2440(String secret) {
|
|
|
|
// Create our message digest for SHA-1
|
|
MessageDigest md;
|
|
try {
|
|
md = MessageDigest.getInstance("SHA-1");
|
|
} catch (Exception ex) {
|
|
return null;
|
|
}
|
|
|
|
// Create our random S2K salt, the 9th byte is loaded with the code count which
|
|
// determines the amount of octet hashing used.
|
|
// This salt is eventually combined with the secret to prevent dictionary attacks
|
|
int codecount = 32;
|
|
byte[] s2ksalt = new byte[9];
|
|
SecureRandom sr = new SecureRandom();
|
|
sr.nextBytes(s2ksalt);
|
|
s2ksalt[8] = (byte) codecount;
|
|
// The octet hashing count is defined by the following formula where EXPBIAS is 6
|
|
int octethashcount = (16 + (codecount & 15)) << ((codecount >> 4) + 6);
|
|
|
|
// Merge our S2K salt and partialsecret arrays together into one array to
|
|
// create the full secret key.
|
|
byte[] partialsecret;
|
|
try {
|
|
partialsecret = secret.getBytes("UTF-8");
|
|
} catch (UnsupportedEncodingException ex) {
|
|
return null;
|
|
}
|
|
byte[] fullsecret = new byte[8 + partialsecret.length];
|
|
System.arraycopy(s2ksalt, 0, fullsecret, 0, 8);
|
|
System.arraycopy(partialsecret, 0, fullsecret, 8, partialsecret.length);
|
|
// Do the hashing based on the hashcount
|
|
while (true) {
|
|
if (octethashcount < fullsecret.length) {
|
|
md.update(fullsecret, 0, octethashcount);
|
|
break;
|
|
} else {
|
|
md.update(fullsecret);
|
|
}
|
|
octethashcount -= fullsecret.length;
|
|
}
|
|
// Create our final key by merging s2kspecifier and digest result
|
|
byte[] ourkey = new byte[20 + 9];
|
|
System.arraycopy(md.digest(), 0, ourkey, 9, 20);
|
|
System.arraycopy(s2ksalt, 0, ourkey, 0, 9);
|
|
return ourkey;
|
|
}
|
|
|
|
/**
|
|
* Get SHAHash using given password string
|
|
*
|
|
* @param password
|
|
* @return SHAhash string
|
|
*/
|
|
public static String getSHAHash(String password) {
|
|
String sha1 = "";
|
|
try {
|
|
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
|
|
crypt.reset();
|
|
crypt.update(password.getBytes("UTF-8"));
|
|
sha1 = byteToHex(crypt.digest());
|
|
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
|
|
}
|
|
return sha1;
|
|
}
|
|
|
|
/**
|
|
* Convert byte to a hex string
|
|
*
|
|
* @param hash
|
|
* @return byte as a hex string
|
|
*/
|
|
private static String byteToHex(final byte[] hash) {
|
|
String result;
|
|
try (Formatter formatter = new Formatter()) {
|
|
for (byte b : hash) {
|
|
formatter.format("%02x", b);
|
|
}
|
|
result = formatter.toString();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Retrieve text from a resource text file
|
|
*
|
|
* @param resourcepath
|
|
* @return String text
|
|
*/
|
|
public static String getTextFromResource(String resourcepath) {
|
|
SimpleFile sf = new SimpleFile(resourcepath);
|
|
sf.openBufferedResource();
|
|
String text = sf.readEntireFile();
|
|
sf.closeFile();
|
|
return text;
|
|
}
|
|
|
|
/**
|
|
* Retrieve text from a text file
|
|
*
|
|
* @param filepath
|
|
* @return String text
|
|
*/
|
|
public static String getTextFromFile(String filepath) {
|
|
SimpleFile sf = new SimpleFile(filepath);
|
|
String text = "";
|
|
if (sf.exists()) {
|
|
sf.openBufferedRead();
|
|
text = sf.readEntireFile();
|
|
sf.closeFile();
|
|
}
|
|
return text;
|
|
}
|
|
|
|
/**
|
|
* Calculate the sha1 checksum of a given file
|
|
*
|
|
* @param filepath
|
|
* @return Checksum as a hex string
|
|
*/
|
|
public static String getSha1Sum(String filepath) {
|
|
StringBuilder sb = new StringBuilder("");
|
|
try {
|
|
MessageDigest md = MessageDigest.getInstance("SHA1");
|
|
FileInputStream fis = new FileInputStream(filepath);
|
|
byte[] dataBytes = new byte[1024];
|
|
int nread;
|
|
while ((nread = fis.read(dataBytes)) != -1) {
|
|
md.update(dataBytes, 0, nread);
|
|
}
|
|
byte[] mdbytes = md.digest();
|
|
//convert the byte to hex format
|
|
for (int i = 0; i < mdbytes.length; i++) {
|
|
sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
|
|
}
|
|
} catch (NoSuchAlgorithmException | IOException ex) {
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
/**
|
|
* Creates a hidden dummy window for registration to various docks/launchers
|
|
* Fixes the neverending launching indicator
|
|
*/
|
|
public static void registerWindow() {
|
|
final JFrame jf = new JFrame();
|
|
|
|
jf.addWindowListener(new java.awt.event.WindowListener() {
|
|
@Override
|
|
public void windowOpened(WindowEvent e) {
|
|
jf.dispose();
|
|
}
|
|
|
|
@Override
|
|
public void windowClosing(WindowEvent e) {
|
|
}
|
|
|
|
@Override
|
|
public void windowClosed(WindowEvent e) {
|
|
}
|
|
|
|
@Override
|
|
public void windowIconified(WindowEvent e) {
|
|
}
|
|
|
|
@Override
|
|
public void windowDeiconified(WindowEvent e) {
|
|
}
|
|
|
|
@Override
|
|
public void windowActivated(WindowEvent e) {
|
|
}
|
|
|
|
@Override
|
|
public void windowDeactivated(WindowEvent e) {
|
|
}
|
|
});
|
|
jf.setUndecorated(true);
|
|
jf.setBounds(0, 0, 0, 0);
|
|
jf.setVisible(true);
|
|
}
|
|
|
|
}
|