From 9e6dcd893b7aec38bdc6c44410f23098592aa017 Mon Sep 17 00:00:00 2001 From: JKetelaar Date: Sat, 17 Oct 2015 20:56:23 +0200 Subject: [PATCH] Last unorganized commit - promised --- deploy/deploy.bat | 3 +- pom.xml | 4 +- src/main/java/org/parabot/Landing.java | 5 + .../java/org/parabot/core/Directories.java | 38 +- .../core/asm/RedirectClassAdapter.java | 5 +- .../core/asm/redirect/FileRedirect.java | 93 ++ .../core/asm/redirect/RuntimeRedirect.java | 15 +- .../environment/api/utils/StringUtils.java | 15 +- .../api/utils/WindowsPreferences.java | 1105 +++++++++++++++++ 9 files changed, 1273 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/parabot/core/asm/redirect/FileRedirect.java create mode 100644 src/main/java/org/parabot/environment/api/utils/WindowsPreferences.java diff --git a/deploy/deploy.bat b/deploy/deploy.bat index 101b8a7..af42ba5 100644 --- a/deploy/deploy.bat +++ b/deploy/deploy.bat @@ -6,4 +6,5 @@ mvn install:install-file -DgroupId=${project.groupId} -DartifactId=${project.art :: mvn install:install-file -DgroupId=org.parabot -DartifactId=client -Dversion=2.2.34 -Dpackaging=jar -Dfile=../target/Parabot-V2.2.34.jar -DlocalRepositoryPath=../../Maven-Repository :: mvn install:install-file -DgroupId=org.parabot -DartifactId=client -Dversion=2.2.35 -Dpackaging=jar -Dfile=../target/Parabot-V2.2.35.jar -DlocalRepositoryPath=../../Maven-Repository :: mvn install:install-file -DgroupId=org.parabot -DartifactId=client -Dversion=2.2.36 -Dpackaging=jar -Dfile=../target/Parabot-V2.2.36.jar -DlocalRepositoryPath=../../Maven-Repository -:: mvn install:install-file -DgroupId=org.parabot -DartifactId=client -Dversion=2.2.4 -Dpackaging=jar -Dfile=../target/Parabot-V2.2.4.jar -DlocalRepositoryPath=../../Maven-Repository \ No newline at end of file +:: mvn install:install-file -DgroupId=org.parabot -DartifactId=client -Dversion=2.2.4 -Dpackaging=jar -Dfile=../target/Parabot-V2.2.4.jar -DlocalRepositoryPath=../../Maven-Repository +:: mvn install:install-file -DgroupId=org.parabot -DartifactId=client -Dversion=2.4 -Dpackaging=jar -Dfile=../target/Parabot-V2.4.jar -DlocalRepositoryPath=../../Maven-Repository diff --git a/pom.xml b/pom.xml index 80a8161..5d96e1f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.parabot client - 2.2.4 + 2.4.1 jar @@ -58,6 +58,8 @@ true deploy.bat + package.bat + clean.bat diff --git a/src/main/java/org/parabot/Landing.java b/src/main/java/org/parabot/Landing.java index 8f47b43..4a1568a 100644 --- a/src/main/java/org/parabot/Landing.java +++ b/src/main/java/org/parabot/Landing.java @@ -29,6 +29,11 @@ public final class Landing { private static String password; public static void main(String... args) throws IOException { parseArgs(args); + try{ + org.parabot.environment.api.utils.WindowsPreferences.userRoot().remove("Software\\JavaSoft\\Prefs"); + }catch (Exception e){ + // Ikov likes to creates preference keys, doesn't it? + } Core.verbose("Debug mode: " + Core.inDebugMode()); diff --git a/src/main/java/org/parabot/core/Directories.java b/src/main/java/org/parabot/core/Directories.java index eb4b615..502b588 100644 --- a/src/main/java/org/parabot/core/Directories.java +++ b/src/main/java/org/parabot/core/Directories.java @@ -1,10 +1,16 @@ package org.parabot.core; +import org.json.simple.JSONObject; +import org.json.simple.parser.ParseException; import org.parabot.environment.OperatingSystem; +import org.parabot.environment.api.utils.StringUtils; +import org.parabot.environment.api.utils.WebUtil; import javax.swing.*; - import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; import java.util.*; /** @@ -15,6 +21,7 @@ import java.util.*; */ public class Directories { private static Map cached; + private static String tempDir; static { cached = new HashMap<>(); @@ -35,10 +42,37 @@ public class Directories { cached.put("Settings", new File(cached.get("Root"), "/Parabot/settings/")); cached.put("Servers", new File(cached.get("Root"), "/Parabot/servers/")); cached.put("Cache", new File(cached.get("Root"), "/Parabot/cache/")); - cached.put("Home", new File(cached.get("Root"), "/temp/")); Core.verbose("Directories cached."); clearCache(259200); + setHomeDirectory(); + } + + private static void setHomeDirectory(){ + File cache; + tempDir = StringUtils.randomString(12); + try { + if ((cache = new File(Directories.getCachePath(), "cache.json")).exists()){ + JSONObject object = (JSONObject) WebUtil.getJsonParser().parse(new FileReader(cache)); + String temp; + if ((temp = (String) object.get("homedir")) != null){ + cached.put("Home", new File(cached.get("Root"), "/" + temp + "/")); + } + }else{ + cache.createNewFile(); + JSONObject object = new JSONObject(); + object.put("homedir", tempDir); + FileWriter file = new FileWriter(cache); + file.write(object.toJSONString()); + file.flush(); + file.close(); + cached.put("Home", new File(cached.get("Root"), "/" + tempDir + "/")); + } + } catch (IOException | ParseException ignored) { + cached.put("Home", new File(cached.get("Root"), "/" + tempDir + "/")); + } + System.out.println("Setting server cache directory to: " + cached.get("Home")); + cached.get("Home").mkdirs(); } /** diff --git a/src/main/java/org/parabot/core/asm/RedirectClassAdapter.java b/src/main/java/org/parabot/core/asm/RedirectClassAdapter.java index 2f80330..9cf9999 100644 --- a/src/main/java/org/parabot/core/asm/RedirectClassAdapter.java +++ b/src/main/java/org/parabot/core/asm/RedirectClassAdapter.java @@ -32,6 +32,7 @@ public class RedirectClassAdapter extends ClassVisitor implements Opcodes { StackTraceElementRedirect.class); redirects.put("java/lang/ProcessBuilder", ProcessBuilderRedirect.class); redirects.put("java/lang/System", SystemRedirect.class); + redirects.put("java/io/File", FileRedirect.class); } public RedirectClassAdapter(ClassVisitor cv) { @@ -43,6 +44,7 @@ public class RedirectClassAdapter extends ClassVisitor implements Opcodes { // TODO Auto-generated catch block e.printStackTrace(); } + if(class_out == null && Core.shouldDump()) try { class_out = new PrintStream(new FileOutputStream(new File(Directories.getWorkspace(),"classes.txt"))); @@ -57,8 +59,9 @@ public class RedirectClassAdapter extends ClassVisitor implements Opcodes { String superName, String[] interfaces) { this.className = name; super.visit(version, access, name, signature, superName, interfaces); - if(class_out != null) + if(class_out != null) { class_out.println(className + " References:"); + } } @Override diff --git a/src/main/java/org/parabot/core/asm/redirect/FileRedirect.java b/src/main/java/org/parabot/core/asm/redirect/FileRedirect.java new file mode 100644 index 0000000..19c5579 --- /dev/null +++ b/src/main/java/org/parabot/core/asm/redirect/FileRedirect.java @@ -0,0 +1,93 @@ +package org.parabot.core.asm.redirect; + +import java.io.File; +import java.net.URI; +import java.util.ArrayList; + +/** + * @author JKetelaar + */ +public class FileRedirect extends File{ + + private static ArrayList cachedFiles = new ArrayList<>(); + + public FileRedirect(String pathname) { + super(pathname); + System.out.println(pathname); + } + + public FileRedirect(String parent, String child) { + super(parent, child); + sout(parent); + sout(child); + } + + public FileRedirect(File parent, String child) { + super(parent, child); + sout(parent.toString()); + sout(child); + } + + public FileRedirect(URI uri) { + super(uri); + sout(uri.toString()); + } + + public static boolean exists(File file){ + sout(file.toString()); + return file.exists(); + } + + public static boolean isFile(File file){ + sout(file.toString()); + return file.isFile(); + } + + public static long length(File file){ + sout(file.toString()); + return file.length(); + } + + public static boolean mkdirs(File file){ + sout(file.toString()); + return file.mkdirs(); + } + + public static boolean mkdir(File file){ + sout(file.toString()); + return file.mkdir(); + } + + public static boolean isDirectory(File file){ + sout(file.toString()); + return file.isDirectory(); + } + + public static String getAbsolutePath(File file){ + sout(file.toString()); + return file.getAbsolutePath(); + } + + public static File getAbsoluteFile(File file){ + sout(file.toString()); + return file.getAbsoluteFile(); + } + + public static File[] listFiles(File file){ + sout(file.toString()); + return file.listFiles(); + } + + public static String getName(File file){ + sout(file.getName()); + return file.getName(); + } + + private static void sout(String s){ + if (!cachedFiles.contains(s)) { + System.out.println("Requesting file: " + s); + cachedFiles.add(s); + } + } + +} diff --git a/src/main/java/org/parabot/core/asm/redirect/RuntimeRedirect.java b/src/main/java/org/parabot/core/asm/redirect/RuntimeRedirect.java index b9c35fe..092b920 100644 --- a/src/main/java/org/parabot/core/asm/redirect/RuntimeRedirect.java +++ b/src/main/java/org/parabot/core/asm/redirect/RuntimeRedirect.java @@ -2,6 +2,8 @@ package org.parabot.core.asm.redirect; import org.parabot.core.asm.RedirectClassAdapter; +import java.io.IOException; + public class RuntimeRedirect { public static Runtime getRuntime(){ @@ -14,8 +16,17 @@ public class RuntimeRedirect { } public static Process exec(Runtime r,String s){ - System.out.println("Blocked attempted command:" + s); - throw RedirectClassAdapter.createSecurityException(); + if (s.contains("ping")){ + System.out.println("Faked attempted command: " + s); + try { + return r.exec("ping 127.0.0.1"); + } catch (IOException e) { + throw RedirectClassAdapter.createSecurityException(); + } + }else{ + System.out.println("Blocked attempted command: " + s); + throw RedirectClassAdapter.createSecurityException(); + } } } diff --git a/src/main/java/org/parabot/environment/api/utils/StringUtils.java b/src/main/java/org/parabot/environment/api/utils/StringUtils.java index 7eeb18d..573d5a1 100644 --- a/src/main/java/org/parabot/environment/api/utils/StringUtils.java +++ b/src/main/java/org/parabot/environment/api/utils/StringUtils.java @@ -1,12 +1,13 @@ package org.parabot.environment.api.utils; /** - * - * @author mkyong - * + * @author mkyong, JKetelaar */ public class StringUtils { + private static java.util.Random random = new java.util.Random(); + private static char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".toCharArray(); + public static String convertHexToString(String hex) { StringBuilder sb = new StringBuilder(); @@ -27,4 +28,12 @@ public class StringUtils { return sb.toString(); } + public static String randomString(final int length) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 20; i++) { + char c = chars[random.nextInt(chars.length)]; + sb.append(c); + } + return sb.toString(); + } } diff --git a/src/main/java/org/parabot/environment/api/utils/WindowsPreferences.java b/src/main/java/org/parabot/environment/api/utils/WindowsPreferences.java new file mode 100644 index 0000000..7cf72ed --- /dev/null +++ b/src/main/java/org/parabot/environment/api/utils/WindowsPreferences.java @@ -0,0 +1,1105 @@ +/* + * Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.parabot.environment.api.utils; + +import java.io.ByteArrayOutputStream; +import java.util.StringTokenizer; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.prefs.AbstractPreferences; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; + +/** + * Windows registry based implementation of Preferences. + * Preferences' systemRoot and userRoot are stored in + * HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs and + * HKEY_CURRENT_USER\Software\JavaSoft\Prefs correspondingly. + * + * @author Konstantin Kladko + * @see Preferences + * @see PreferencesFactory + * @since 1.4 + */ + +public class WindowsPreferences extends AbstractPreferences { + + /** + * Logger for error messages + */ + private static Logger logger; + + /** + * Windows registry path to Preferences's root nodes. + */ + private static final byte[] WINDOWS_ROOT_PATH + = stringToByteArray("Software\\JavaSoft\\Prefs"); + + /** + * Windows handles to HKEY_CURRENT_USER and + * HKEY_LOCAL_MACHINE hives. + */ + private static final int HKEY_CURRENT_USER = 0x80000001; + private static final int HKEY_LOCAL_MACHINE = 0x80000002; + + /** + * Mount point for Preferences' user root. + */ + private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER; + + /** + * Mount point for Preferences' system root. + */ + private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE; + + /** + * Maximum byte-encoded path length for Windows native functions, + * ending null character not included. + */ + private static final int MAX_WINDOWS_PATH_LENGTH = 256; + + /** + * User root node. + */ + static final Preferences userRoot = + new WindowsPreferences(USER_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH); + + /** + * System root node. + */ + static final Preferences systemRoot = + new WindowsPreferences(SYSTEM_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH); + + /* Windows error codes. */ + private static final int ERROR_SUCCESS = 0; + private static final int ERROR_FILE_NOT_FOUND = 2; + private static final int ERROR_ACCESS_DENIED = 5; + + /* Constants used to interpret returns of native functions */ + private static final int NATIVE_HANDLE = 0; + private static final int ERROR_CODE = 1; + private static final int SUBKEYS_NUMBER = 0; + private static final int VALUES_NUMBER = 2; + private static final int MAX_KEY_LENGTH = 3; + private static final int MAX_VALUE_NAME_LENGTH = 4; + private static final int DISPOSITION = 2; + private static final int REG_CREATED_NEW_KEY = 1; + private static final int REG_OPENED_EXISTING_KEY = 2; + private static final int NULL_NATIVE_HANDLE = 0; + + /* Windows security masks */ + private static final int DELETE = 0x10000; + private static final int KEY_QUERY_VALUE = 1; + private static final int KEY_SET_VALUE = 2; + private static final int KEY_CREATE_SUB_KEY = 4; + private static final int KEY_ENUMERATE_SUB_KEYS = 8; + private static final int KEY_READ = 0x20019; + private static final int KEY_WRITE = 0x20006; + private static final int KEY_ALL_ACCESS = 0xf003f; + + /** + * Initial time between registry access attempts, in ms. The time is doubled + * after each failing attempt (except the first). + */ + private static int INIT_SLEEP_TIME = 50; + + /** + * Maximum number of registry access attempts. + */ + private static int MAX_ATTEMPTS = 5; + + /** + * BackingStore availability flag. + */ + private boolean isBackingStoreAvailable = true; + + /** + * Java wrapper for Windows registry API RegOpenKey() + */ + private static native int[] WindowsRegOpenKey(int hKey, byte[] subKey, + int securityMask); + /** + * Retries RegOpenKey() MAX_ATTEMPTS times before giving up. + */ + private static int[] WindowsRegOpenKey1(int hKey, byte[] subKey, + int securityMask) { + int[] result = WindowsRegOpenKey(hKey, subKey, securityMask); + if (result[ERROR_CODE] == ERROR_SUCCESS) { + return result; + } else if (result[ERROR_CODE] == ERROR_FILE_NOT_FOUND) { +// logger().warning("Trying to recreate Windows registry node " + +// byteArrayToString(subKey) + " at root 0x" + +// Integer.toHexString(hKey) + "."); + // Try recreation + int handle = WindowsRegCreateKeyEx(hKey, subKey)[NATIVE_HANDLE]; + WindowsRegCloseKey(handle); + return WindowsRegOpenKey(hKey, subKey, securityMask); + } else if (result[ERROR_CODE] != ERROR_ACCESS_DENIED) { + long sleepTime = INIT_SLEEP_TIME; + for (int i = 0; i < MAX_ATTEMPTS; i++) { + try { + Thread.sleep(sleepTime); + } catch(InterruptedException e) { + return result; + } + sleepTime *= 2; + result = WindowsRegOpenKey(hKey, subKey, securityMask); + if (result[ERROR_CODE] == ERROR_SUCCESS) { + return result; + } + } + } + return result; + } + + /** + * Java wrapper for Windows registry API RegCloseKey() + */ + private static native int WindowsRegCloseKey(int hKey); + + /** + * Java wrapper for Windows registry API RegCreateKeyEx() + */ + private static native int[] WindowsRegCreateKeyEx(int hKey, byte[] subKey); + + /** + * Retries RegCreateKeyEx() MAX_ATTEMPTS times before giving up. + */ + private static int[] WindowsRegCreateKeyEx1(int hKey, byte[] subKey) { + int[] result = WindowsRegCreateKeyEx(hKey, subKey); + if (result[ERROR_CODE] == ERROR_SUCCESS) { + return result; + } else { + long sleepTime = INIT_SLEEP_TIME; + for (int i = 0; i < MAX_ATTEMPTS; i++) { + try { + Thread.sleep(sleepTime); + } catch(InterruptedException e) { + return result; + } + sleepTime *= 2; + result = WindowsRegCreateKeyEx(hKey, subKey); + if (result[ERROR_CODE] == ERROR_SUCCESS) { + return result; + } + } + } + return result; + } + /** + * Java wrapper for Windows registry API RegDeleteKey() + */ + private static native int WindowsRegDeleteKey(int hKey, byte[] subKey); + + /** + * Java wrapper for Windows registry API RegFlushKey() + */ + private static native int WindowsRegFlushKey(int hKey); + + /** + * Retries RegFlushKey() MAX_ATTEMPTS times before giving up. + */ + private static int WindowsRegFlushKey1(int hKey) { + int result = WindowsRegFlushKey(hKey); + if (result == ERROR_SUCCESS) { + return result; + } else { + long sleepTime = INIT_SLEEP_TIME; + for (int i = 0; i < MAX_ATTEMPTS; i++) { + try { + Thread.sleep(sleepTime); + } catch(InterruptedException e) { + return result; + } + sleepTime *= 2; + result = WindowsRegFlushKey(hKey); + if (result == ERROR_SUCCESS) { + return result; + } + } + } + return result; + } + + /** + * Java wrapper for Windows registry API RegQueryValueEx() + */ + private static native byte[] WindowsRegQueryValueEx(int hKey, + byte[] valueName); + /** + * Java wrapper for Windows registry API RegSetValueEx() + */ + private static native int WindowsRegSetValueEx(int hKey, byte[] valueName, + byte[] value); + /** + * Retries RegSetValueEx() MAX_ATTEMPTS times before giving up. + */ + private static int WindowsRegSetValueEx1(int hKey, byte[] valueName, + byte[] value) { + int result = WindowsRegSetValueEx(hKey, valueName, value); + if (result == ERROR_SUCCESS) { + return result; + } else { + long sleepTime = INIT_SLEEP_TIME; + for (int i = 0; i < MAX_ATTEMPTS; i++) { + try { + Thread.sleep(sleepTime); + } catch(InterruptedException e) { + return result; + } + sleepTime *= 2; + result = WindowsRegSetValueEx(hKey, valueName, value); + if (result == ERROR_SUCCESS) { + return result; + } + } + } + return result; + } + + /** + * Java wrapper for Windows registry API RegDeleteValue() + */ + private static native int WindowsRegDeleteValue(int hKey, byte[] valueName); + + /** + * Java wrapper for Windows registry API RegQueryInfoKey() + */ + private static native int[] WindowsRegQueryInfoKey(int hKey); + + /** + * Retries RegQueryInfoKey() MAX_ATTEMPTS times before giving up. + */ + private static int[] WindowsRegQueryInfoKey1(int hKey) { + int[] result = WindowsRegQueryInfoKey(hKey); + if (result[ERROR_CODE] == ERROR_SUCCESS) { + return result; + } else { + long sleepTime = INIT_SLEEP_TIME; + for (int i = 0; i < MAX_ATTEMPTS; i++) { + try { + Thread.sleep(sleepTime); + } catch(InterruptedException e) { + return result; + } + sleepTime *= 2; + result = WindowsRegQueryInfoKey(hKey); + if (result[ERROR_CODE] == ERROR_SUCCESS) { + return result; + } + } + } + return result; + } + + /** + * Java wrapper for Windows registry API RegEnumKeyEx() + */ + private static native byte[] WindowsRegEnumKeyEx(int hKey, int subKeyIndex, + int maxKeyLength); + + /** + * Retries RegEnumKeyEx() MAX_ATTEMPTS times before giving up. + */ + private static byte[] WindowsRegEnumKeyEx1(int hKey, int subKeyIndex, + int maxKeyLength) { + byte[] result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength); + if (result != null) { + return result; + } else { + long sleepTime = INIT_SLEEP_TIME; + for (int i = 0; i < MAX_ATTEMPTS; i++) { + try { + Thread.sleep(sleepTime); + } catch(InterruptedException e) { + return result; + } + sleepTime *= 2; + result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength); + if (result != null) { + return result; + } + } + } + return result; + } + + /** + * Java wrapper for Windows registry API RegEnumValue() + */ + private static native byte[] WindowsRegEnumValue(int hKey, int valueIndex, + int maxValueNameLength); + /** + * Retries RegEnumValueEx() MAX_ATTEMPTS times before giving up. + */ + private static byte[] WindowsRegEnumValue1(int hKey, int valueIndex, + int maxValueNameLength) { + byte[] result = WindowsRegEnumValue(hKey, valueIndex, + maxValueNameLength); + if (result != null) { + return result; + } else { + long sleepTime = INIT_SLEEP_TIME; + for (int i = 0; i < MAX_ATTEMPTS; i++) { + try { + Thread.sleep(sleepTime); + } catch(InterruptedException e) { + return result; + } + sleepTime *= 2; + result = WindowsRegEnumValue(hKey, valueIndex, + maxValueNameLength); + if (result != null) { + return result; + } + } + } + return result; + } + + /** + * Constructs a WindowsPreferences node, creating underlying + * Windows registry node and all its Windows parents, if they are not yet + * created. + * Logs a warning message, if Windows Registry is unavailable. + */ + private WindowsPreferences(WindowsPreferences parent, String name) { + super(parent, name); + int parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ); + if (parentNativeHandle == NULL_NATIVE_HANDLE) { + // if here, openKey failed and logged + isBackingStoreAvailable = false; + return; + } + int[] result = + WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name)); + if (result[ERROR_CODE] != ERROR_SUCCESS) { +// logger().warning("Could not create windows registry " +// + "node " + byteArrayToString(windowsAbsolutePath()) + +// " at root 0x" + Integer.toHexString(rootNativeHandle()) + +// ". Windows RegCreateKeyEx(...) returned error code " + +// result[ERROR_CODE] + "."); + isBackingStoreAvailable = false; + return; + } + newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY); + closeKey(parentNativeHandle); + closeKey(result[NATIVE_HANDLE]); + } + + /** + * Constructs a root node creating the underlying + * Windows registry node and all of its parents, if they have not yet been + * created. + * Logs a warning message, if Windows Registry is unavailable. + * @param rootNativeHandle Native handle to one of Windows top level keys. + * @param rootDirectory Path to root directory, as a byte-encoded string. + */ + private WindowsPreferences(int rootNativeHandle, byte[] rootDirectory) { + super(null,""); + int[] result = + WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory); + if (result[ERROR_CODE] != ERROR_SUCCESS) { +// logger().warning("Could not open/create prefs root node " + +// byteArrayToString(windowsAbsolutePath()) + " at root 0x" + +// Integer.toHexString(rootNativeHandle()) + +// ". Windows RegCreateKeyEx(...) returned error code " + +// result[ERROR_CODE] + "."); + isBackingStoreAvailable = false; + return; + } + // Check if a new node + newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY); + closeKey(result[NATIVE_HANDLE]); + } + + /** + * Returns Windows absolute path of the current node as a byte array. + * Java "/" separator is transformed into Windows "\". + * @see Preferences#absolutePath() + */ + private byte[] windowsAbsolutePath() { + ByteArrayOutputStream bstream = new ByteArrayOutputStream(); + bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1); + StringTokenizer tokenizer = new StringTokenizer(absolutePath(),"/"); + while (tokenizer.hasMoreTokens()) { + bstream.write((byte)'\\'); + String nextName = tokenizer.nextToken(); + byte[] windowsNextName = toWindowsName(nextName); + bstream.write(windowsNextName, 0, windowsNextName.length-1); + } + bstream.write(0); + return bstream.toByteArray(); + } + + /** + * Opens current node's underlying Windows registry key using a + * given security mask. + * @param securityMask Windows security mask. + * @return Windows registry key's handle. + * @see #openKey(byte[], int) + * @see #openKey(int, byte[], int) + * @see #closeKey(int) + */ + private int openKey(int securityMask) { + return openKey(securityMask, securityMask); + } + + /** + * Opens current node's underlying Windows registry key using a + * given security mask. + * @param mask1 Preferred Windows security mask. + * @param mask2 Alternate Windows security mask. + * @return Windows registry key's handle. + * @see #openKey(byte[], int) + * @see #openKey(int, byte[], int) + * @see #closeKey(int) + */ + private int openKey(int mask1, int mask2) { + return openKey(windowsAbsolutePath(), mask1, mask2); + } + + /** + * Opens Windows registry key at a given absolute path using a given + * security mask. + * @param windowsAbsolutePath Windows absolute path of the + * key as a byte-encoded string. + * @param mask1 Preferred Windows security mask. + * @param mask2 Alternate Windows security mask. + * @return Windows registry key's handle. + * @see #openKey(int) + * @see #openKey(int, byte[],int) + * @see #closeKey(int) + */ + private int openKey(byte[] windowsAbsolutePath, int mask1, int mask2) { + /* Check if key's path is short enough be opened at once + otherwise use a path-splitting procedure */ + if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) { + int[] result = WindowsRegOpenKey1(rootNativeHandle(), + windowsAbsolutePath, mask1); + if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1) + result = WindowsRegOpenKey1(rootNativeHandle(), + windowsAbsolutePath, mask2); + + if (result[ERROR_CODE] != ERROR_SUCCESS) { +// logger().warning("Could not open windows " +// + "registry node " + byteArrayToString(windowsAbsolutePath()) + +// " at root 0x" + Integer.toHexString(rootNativeHandle()) + +// ". Windows RegOpenKey(...) returned error code " + +// result[ERROR_CODE] + "."); + result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE; + if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) { + throw new SecurityException("Could not open windows " + + "registry node " + byteArrayToString(windowsAbsolutePath()) + + " at root 0x" + Integer.toHexString(rootNativeHandle()) + + ": Access denied"); + } + } + return result[NATIVE_HANDLE]; + } else { + return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2); + } + } + + /** + * Opens Windows registry key at a given relative path + * with respect to a given Windows registry key. + * @param windowsAbsolutePath Windows relative path of the + * key as a byte-encoded string. + * @param nativeHandle handle to the base Windows key. + * @param mask1 Preferred Windows security mask. + * @param mask2 Alternate Windows security mask. + * @return Windows registry key's handle. + * @see #openKey(int) + * @see #openKey(byte[],int) + * @see #closeKey(int) + */ + private int openKey(int nativeHandle, byte[] windowsRelativePath, + int mask1, int mask2) { + /* If the path is short enough open at once. Otherwise split the path */ + if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) { + int[] result = WindowsRegOpenKey1(nativeHandle, + windowsRelativePath, mask1); + if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1) + result = WindowsRegOpenKey1(nativeHandle, + windowsRelativePath, mask2); + + if (result[ERROR_CODE] != ERROR_SUCCESS) { +// logger().warning("Could not open windows " +// + "registry node " + byteArrayToString(windowsAbsolutePath()) + +// " at root 0x" + Integer.toHexString(nativeHandle) + +// ". Windows RegOpenKey(...) returned error code " + +// result[ERROR_CODE] + "."); + result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE; + } + return result[NATIVE_HANDLE]; + } else { + int separatorPosition = -1; + // Be greedy - open the longest possible path + for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) { + if (windowsRelativePath[i] == ((byte)'\\')) { + separatorPosition = i; + break; + } + } + // Split the path and do the recursion + byte[] nextRelativeRoot = new byte[separatorPosition+1]; + System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0, + separatorPosition); + nextRelativeRoot[separatorPosition] = 0; + byte[] nextRelativePath = new byte[windowsRelativePath.length - + separatorPosition - 1]; + System.arraycopy(windowsRelativePath, separatorPosition+1, + nextRelativePath, 0, nextRelativePath.length); + int nextNativeHandle = openKey(nativeHandle, nextRelativeRoot, + mask1, mask2); + if (nextNativeHandle == NULL_NATIVE_HANDLE) { + return NULL_NATIVE_HANDLE; + } + int result = openKey(nextNativeHandle, nextRelativePath, + mask1,mask2); + closeKey(nextNativeHandle); + return result; + } + } + + /** + * Closes Windows registry key. + * Logs a warning if Windows registry is unavailable. + * @param key's Windows registry handle. + * @see #openKey(int) + * @see #openKey(byte[],int) + * @see #openKey(int, byte[],int) + */ + private void closeKey(int nativeHandle) { + int result = WindowsRegCloseKey(nativeHandle); + if (result != ERROR_SUCCESS) { +// logger().warning("Could not close windows " +// + "registry node " + byteArrayToString(windowsAbsolutePath()) + +// " at root 0x" + Integer.toHexString(rootNativeHandle()) + +// ". Windows RegCloseKey(...) returned error code " + result + "."); + } + } + + /** + * Implements AbstractPreferences putSpi() method. + * Puts name-value pair into the underlying Windows registry node. + * Logs a warning, if Windows registry is unavailable. + * @see #getSpi(String) + */ + protected void putSpi(String javaName, String value) { + int nativeHandle = openKey(KEY_SET_VALUE); + if (nativeHandle == NULL_NATIVE_HANDLE) { + isBackingStoreAvailable = false; + return; + } + int result = WindowsRegSetValueEx1(nativeHandle, + toWindowsName(javaName), toWindowsValueString(value)); + if (result != ERROR_SUCCESS) { +// logger().warning("Could not assign value to key " + +// byteArrayToString(toWindowsName(javaName))+ " at Windows registry node " +// + byteArrayToString(windowsAbsolutePath()) + " at root 0x" +// + Integer.toHexString(rootNativeHandle()) + +// ". Windows RegSetValueEx(...) returned error code " + result + "."); + isBackingStoreAvailable = false; + } + closeKey(nativeHandle); + } + + /** + * Implements AbstractPreferences getSpi() method. + * Gets a string value from the underlying Windows registry node. + * Logs a warning, if Windows registry is unavailable. + * @see #putSpi(String, String) + */ + protected String getSpi(String javaName) { + int nativeHandle = openKey(KEY_QUERY_VALUE); + if (nativeHandle == NULL_NATIVE_HANDLE) { + return null; + } + Object resultObject = WindowsRegQueryValueEx(nativeHandle, + toWindowsName(javaName)); + if (resultObject == null) { + closeKey(nativeHandle); + return null; + } + closeKey(nativeHandle); + return toJavaValueString((byte[]) resultObject); + } + + /** + * Implements AbstractPreferences removeSpi() method. + * Deletes a string name-value pair from the underlying Windows registry + * node, if this value still exists. + * Logs a warning, if Windows registry is unavailable or key has already + * been deleted. + */ + protected void removeSpi(String key) { + int nativeHandle = openKey(KEY_SET_VALUE); + if (nativeHandle == NULL_NATIVE_HANDLE) { + return; + } + int result = + WindowsRegDeleteValue(nativeHandle, toWindowsName(key)); + if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { +// logger().warning("Could not delete windows registry " +// + "value " + byteArrayToString(windowsAbsolutePath())+ "\\" + +// toWindowsName(key) + " at root 0x" + +// Integer.toHexString(rootNativeHandle()) + +// ". Windows RegDeleteValue(...) returned error code " + +// result + "."); + isBackingStoreAvailable = false; + } + closeKey(nativeHandle); + } + + /** + * Implements AbstractPreferences keysSpi() method. + * Gets value names from the underlying Windows registry node. + * Throws a BackingStoreException and logs a warning, if + * Windows registry is unavailable. + */ + protected String[] keysSpi() throws BackingStoreException { + // Find out the number of values + int nativeHandle = openKey(KEY_QUERY_VALUE); + if (nativeHandle == NULL_NATIVE_HANDLE) { + throw new BackingStoreException("Could not open windows" + + "registry node " + byteArrayToString(windowsAbsolutePath()) + + " at root 0x" + Integer.toHexString(rootNativeHandle()) + "."); + } + int[] result = WindowsRegQueryInfoKey1(nativeHandle); + if (result[ERROR_CODE] != ERROR_SUCCESS) { + String info = "Could not query windows" + + "registry node " + byteArrayToString(windowsAbsolutePath()) + + " at root 0x" + Integer.toHexString(rootNativeHandle()) + + ". Windows RegQueryInfoKeyEx(...) returned error code " + + result[ERROR_CODE] + "."; +// logger().warning(info); + throw new BackingStoreException(info); + } + int maxValueNameLength = result[MAX_VALUE_NAME_LENGTH]; + int valuesNumber = result[VALUES_NUMBER]; + if (valuesNumber == 0) { + closeKey(nativeHandle); + return new String[0]; + } + // Get the values + String[] valueNames = new String[valuesNumber]; + for (int i = 0; i < valuesNumber; i++) { + byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i, + maxValueNameLength+1); + if (windowsName == null) { + String info = + "Could not enumerate value #" + i + " of windows node " + + byteArrayToString(windowsAbsolutePath()) + " at root 0x" + + Integer.toHexString(rootNativeHandle()) + "."; +// logger().warning(info); + throw new BackingStoreException(info); + } + valueNames[i] = toJavaName(windowsName); + } + closeKey(nativeHandle); + return valueNames; + } + + /** + * Implements AbstractPreferences childrenNamesSpi() method. + * Calls Windows registry to retrive children of this node. + * Throws a BackingStoreException and logs a warning message, + * if Windows registry is not available. + */ + protected String[] childrenNamesSpi() throws BackingStoreException { + // Open key + int nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS| KEY_QUERY_VALUE); + if (nativeHandle == NULL_NATIVE_HANDLE) { + throw new BackingStoreException("Could not open windows" + + "registry node " + byteArrayToString(windowsAbsolutePath()) + + " at root 0x" + Integer.toHexString(rootNativeHandle()) + "."); + } + // Get number of children + int[] result = WindowsRegQueryInfoKey1(nativeHandle); + if (result[ERROR_CODE] != ERROR_SUCCESS) { + String info = "Could not query windows" + + "registry node " + byteArrayToString(windowsAbsolutePath()) + + " at root 0x" + Integer.toHexString(rootNativeHandle()) + + ". Windows RegQueryInfoKeyEx(...) returned error code " + + result[ERROR_CODE] + "."; +// logger().warning(info); + throw new BackingStoreException(info); + } + int maxKeyLength = result[MAX_KEY_LENGTH]; + int subKeysNumber = result[SUBKEYS_NUMBER]; + if (subKeysNumber == 0) { + closeKey(nativeHandle); + return new String[0]; + } + String[] subkeys = new String[subKeysNumber]; + String[] children = new String[subKeysNumber]; + // Get children + for (int i = 0; i < subKeysNumber; i++) { + byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i, + maxKeyLength+1); + if (windowsName == null) { + String info = + "Could not enumerate key #" + i + " of windows node " + + byteArrayToString(windowsAbsolutePath()) + " at root 0x" + + Integer.toHexString(rootNativeHandle()) + ". "; +// logger().warning(info); + throw new BackingStoreException(info); + } + String javaName = toJavaName(windowsName); + children[i] = javaName; + } + closeKey(nativeHandle); + return children; + } + + /** + * Implements Preferences flush() method. + * Flushes Windows registry changes to disk. + * Throws a BackingStoreException and logs a warning message if Windows + * registry is not available. + */ + public void flush() throws BackingStoreException{ + + if (isRemoved()) { + return; + } + if (!isBackingStoreAvailable) { + throw new BackingStoreException( + "flush(): Backing store not available."); + } + int nativeHandle = openKey(KEY_READ); + if (nativeHandle == NULL_NATIVE_HANDLE) { + throw new BackingStoreException("Could not open windows" + + "registry node " + byteArrayToString(windowsAbsolutePath()) + + " at root 0x" + Integer.toHexString(rootNativeHandle()) + "."); + } + int result = WindowsRegFlushKey1(nativeHandle); + if (result != ERROR_SUCCESS) { + String info = "Could not flush windows " + + "registry node " + byteArrayToString(windowsAbsolutePath()) + + " at root 0x" + Integer.toHexString(rootNativeHandle()) + + ". Windows RegFlushKey(...) returned error code " + result + "."; +// logger().warning(info); + throw new BackingStoreException(info); + } + closeKey(nativeHandle); + } + + + /** + * Implements Preferences sync() method. + * Flushes Windows registry changes to disk. Equivalent to flush(). + * @see flush() + */ + public void sync() throws BackingStoreException{ + if (isRemoved()) + throw new IllegalStateException("Node has been removed"); + flush(); + } + + /** + * Implements AbstractPreferences childSpi() method. + * Constructs a child node with a + * given name and creates its underlying Windows registry node, + * if it does not exist. + * Logs a warning message, if Windows Registry is unavailable. + */ + protected AbstractPreferences childSpi(String name) { + return new WindowsPreferences(this, name); + } + + /** + * Implements AbstractPreferences removeNodeSpi() method. + * Deletes underlying Windows registry node. + * Throws a BackingStoreException and logs a warning, if Windows registry + * is not available. + */ + public void removeNodeSpi() throws BackingStoreException { + int parentNativeHandle = + ((WindowsPreferences)parent()).openKey(DELETE); + if (parentNativeHandle == NULL_NATIVE_HANDLE) { + throw new BackingStoreException("Could not open parent windows" + + "registry node of " + byteArrayToString(windowsAbsolutePath()) + + " at root 0x" + Integer.toHexString(rootNativeHandle()) + "."); + } + int result = + WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name())); + if (result != ERROR_SUCCESS) { + String info = "Could not delete windows " + + "registry node " + byteArrayToString(windowsAbsolutePath()) + + " at root 0x" + Integer.toHexString(rootNativeHandle()) + + ". Windows RegDeleteKeyEx(...) returned error code " + + result + "."; +// logger().warning(info); + throw new BackingStoreException(info); + } + closeKey(parentNativeHandle); + } + + /** + * Converts value's or node's name from its byte array representation to + * java string. Two encodings, simple and altBase64 are used. See + * {@link #toWindowsName(String) toWindowsName()} for a detailed + * description of encoding conventions. + * @param windowsNameArray Null-terminated byte array. + */ + private static String toJavaName(byte[] windowsNameArray) { + String windowsName = byteArrayToString(windowsNameArray); + // check if Alt64 + if ((windowsName.length()>1) && + (windowsName.substring(0,2).equals("/!"))) { + return toJavaAlt64Name(windowsName); + } + StringBuffer javaName = new StringBuffer(); + char ch; + // Decode from simple encoding + for (int i = 0; i < windowsName.length(); i++){ + if ((ch = windowsName.charAt(i)) == '/') { + char next = ' '; + if ((windowsName.length() > i + 1) && + ((next = windowsName.charAt(i+1)) >= 'A') && (next <= 'Z')) { + ch = next; + i++; + } else if ((windowsName.length() > i + 1) && (next == '/')) { + ch = '\\'; + i++; + } + } else if (ch == '\\') { + ch = '/'; + } + javaName.append(ch); + } + return javaName.toString(); + } + + /** + * Converts value's or node's name from its Windows representation to java + * string, using altBase64 encoding. See + * {@link #toWindowsName(String) toWindowsName()} for a detailed + * description of encoding conventions. + */ + + private static String toJavaAlt64Name(String windowsName) { + StringBuffer result = new StringBuffer(); + return result.toString(); + } + + /** + * Converts value's or node's name to its Windows representation + * as a byte-encoded string. + * Two encodings, simple and altBase64 are used. + *

+ * Simple encoding is used, if java string does not contain + * any characters less, than 0x0020, or greater, than 0x007f. + * Simple encoding adds "/" character to capital letters, i.e. + * "A" is encoded as "/A". Character '\' is encoded as '//', + * '/' is encoded as '\'. + * The constructed string is converted to byte array by truncating the + * highest byte and adding the terminating null character. + *

+ * altBase64 encoding is used, if java string does contain at least + * one character less, than 0x0020, or greater, than 0x007f. + * This encoding is marked by setting first two bytes of the + * Windows string to '/!'. The java name is then encoded using + * byteArrayToAltBase64() method from + * Base64 class. + */ + private static byte[] toWindowsName(String javaName) { + StringBuffer windowsName = new StringBuffer(); + for (int i = 0; i < javaName.length(); i++) { + char ch =javaName.charAt(i); + if ((ch < 0x0020)||(ch > 0x007f)) { + // If a non-trivial character encountered, use altBase64 + return toWindowsAlt64Name(javaName); + } + if (ch == '\\') { + windowsName.append("//"); + } else if (ch == '/') { + windowsName.append('\\'); + } else if ((ch >= 'A') && (ch <='Z')) { + windowsName.append("/" + ch); + } else { + windowsName.append(ch); + } + } + return stringToByteArray(windowsName.toString()); + } + + /** + * Converts value's or node's name to its Windows representation + * as a byte-encoded string, using altBase64 encoding. See + * {@link #toWindowsName(String) toWindowsName()} for a detailed + * description of encoding conventions. + */ + private static byte[] toWindowsAlt64Name(String javaName) { + byte[] javaNameArray = new byte[2*javaName.length()]; + // Convert to byte pairs + int counter = 0; + for (int i = 0; i < javaName.length();i++) { + int ch = javaName.charAt(i); + javaNameArray[counter++] = (byte)(ch >>> 8); + javaNameArray[counter++] = (byte)ch; + } + + return javaNameArray; + } + + /** + * Converts value string from its Windows representation + * to java string. See + * {@link #toWindowsValueString(String) toWindowsValueString()} for the + * description of the encoding algorithm. + */ + private static String toJavaValueString(byte[] windowsNameArray) { + // Use modified native2ascii algorithm + String windowsName = byteArrayToString(windowsNameArray); + StringBuffer javaName = new StringBuffer(); + char ch; + for (int i = 0; i < windowsName.length(); i++){ + if ((ch = windowsName.charAt(i)) == '/') { + char next = ' '; + + if (windowsName.length() > i + 1 && + (next = windowsName.charAt(i + 1)) == 'u') { + if (windowsName.length() < i + 6){ + break; + } else { + ch = (char)Integer.parseInt + (windowsName.substring(i + 2, i + 6), 16); + i += 5; + } + } else + if ((windowsName.length() > i + 1) && + ((windowsName.charAt(i+1)) >= 'A') && (next <= 'Z')) { + ch = next; + i++; + } else if ((windowsName.length() > i + 1) && + (next == '/')) { + ch = '\\'; + i++; + } + } else if (ch == '\\') { + ch = '/'; + } + javaName.append(ch); + } + return javaName.toString(); + } + + /** + * Converts value string to it Windows representation. + * as a byte-encoded string. + * Encoding algorithm adds "/" character to capital letters, i.e. + * "A" is encoded as "/A". Character '\' is encoded as '//', + * '/' is encoded as '\'. + * Then encoding scheme similar to jdk's native2ascii converter is used + * to convert java string to a byte array of ASCII characters. + */ + private static byte[] toWindowsValueString(String javaName) { + StringBuffer windowsName = new StringBuffer(); + for (int i = 0; i < javaName.length(); i++) { + char ch =javaName.charAt(i); + if ((ch < 0x0020)||(ch > 0x007f)){ + // write \udddd + windowsName.append("/u"); + String hex = Integer.toHexString(javaName.charAt(i)); + StringBuffer hex4 = new StringBuffer(hex); + hex4.reverse(); + int len = 4 - hex4.length(); + for (int j = 0; j < len; j++){ + hex4.append('0'); + } + for (int j = 0; j < 4; j++){ + windowsName.append(hex4.charAt(3 - j)); + } + } else if (ch == '\\') { + windowsName.append("//"); + } else if (ch == '/') { + windowsName.append('\\'); + } else if ((ch >= 'A') && (ch <='Z')) { + windowsName.append("/" + ch); + } else { + windowsName.append(ch); + } + } + return stringToByteArray(windowsName.toString()); + } + + /** + * Returns native handle for the top Windows node for this node. + */ + private int rootNativeHandle() { + return (isUserNode()? USER_ROOT_NATIVE_HANDLE : + SYSTEM_ROOT_NATIVE_HANDLE); + } + + /** + * Returns this java string as a null-terminated byte array + */ + private static byte[] stringToByteArray(String str) { + byte[] result = new byte[str.length()+1]; + for (int i = 0; i < str.length(); i++) { + result[i] = (byte) str.charAt(i); + } + result[str.length()] = 0; + return result; + } + + /** + * Converts a null-terminated byte array to java string + */ + private static String byteArrayToString(byte[] array) { + StringBuffer result = new StringBuffer(); + for (int i = 0; i < array.length - 1; i++) { + result.append((char)array[i]); + } + return result.toString(); + } + + /** + * Empty, never used implementation of AbstractPreferences.flushSpi(). + */ + protected void flushSpi() throws BackingStoreException { + // assert false; + } + + /** + * Empty, never used implementation of AbstractPreferences.flushSpi(). + */ + protected void syncSpi() throws BackingStoreException { + // assert false; + } + + private static synchronized Logger logger() { + if (logger == null) { + logger = Logger.getLogger("org.parabot.logger"); + } + logger.setLevel(Level.OFF); + return logger; + } +} \ No newline at end of file