/* * Copyright (C) 2000 - 2011 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD 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 OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://www.openbluedragon.org/ */ package com.naryx.tagfusion.cfm.registry; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import com.nary.util.FastMap; import com.nary.util.string; import com.naryx.tagfusion.cfm.engine.catchDataFactory; import com.naryx.tagfusion.cfm.engine.cfData; import com.naryx.tagfusion.cfm.engine.cfEngine; import com.naryx.tagfusion.cfm.engine.cfNumberData; import com.naryx.tagfusion.cfm.engine.cfQueryResultData; import com.naryx.tagfusion.cfm.engine.cfSession; import com.naryx.tagfusion.cfm.engine.cfStringData; import com.naryx.tagfusion.cfm.engine.cfmBadFileException; import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; import com.naryx.tagfusion.cfm.tag.cfTag; import com.naryx.tagfusion.cfm.tag.cfTagReturnType; import com.naryx.tagfusion.xmlConfig.xmlCFML; import com.newatlanta.jni.registry.NoSuchValueException; import com.newatlanta.jni.registry.RegDWordValue; import com.newatlanta.jni.registry.RegStringValue; import com.newatlanta.jni.registry.Registry; import com.newatlanta.jni.registry.RegistryException; import com.newatlanta.jni.registry.RegistryKey; import com.newatlanta.jni.registry.RegistryValue; public class cfREGISTRY extends cfTag implements Serializable { static final long serialVersionUID = 1; public static final int STRING_TYPE = 0; public static final int DWORD_TYPE = 1; public static final int KEY_TYPE = 2; public static final int ANY_TYPE = 3; /* * init */ public static void init(xmlCFML configFile) { try { String nativeLibPath = cfEngine.getNativeLibDirectory(); if (nativeLibPath != null) { nativeLibPath = nativeLibPath + "cfregistry.dll"; System.load(com.nary.io.FileUtils.resolveNativeLibPath(nativeLibPath)); cfEngine.log("-] Loaded cfregistry.dll >> [" + nativeLibPath + "]"); } else { cfEngine.log("-] ERROR - failed to initialize CFREGISTRY because nativeLibPath is null."); } } catch (Throwable t) { cfEngine.log("-] ERROR - failed to initialize CFREGISTRY."); } } /* * defaultParameters */ protected void defaultParameters(String _tag) throws cfmBadFileException { defaultAttribute("TYPE", "STRING"); defaultAttribute("VALUE", ""); parseTagHeader(_tag); if (!containsAttribute("ACTION")) throw newBadFileException("Missing ACTION", "You need to provide a ACTION"); if (!containsAttribute("BRANCH")) throw newBadFileException("Missing BRANCH", "You need to provide a BRANCH"); } /* * render */ public cfTagReturnType render(cfSession _Session) throws cfmRunTimeException { String ACTION = getDynamic(_Session, "ACTION").getString().toLowerCase(); String BRANCH = getDynamic(_Session, "BRANCH").getString().toUpperCase(); if (ACTION.equals("getall")) regGetAll(_Session, BRANCH); else if (ACTION.equals("get")) regGet(_Session, BRANCH); else if (ACTION.equals("set")) regSet(_Session, BRANCH); else if (ACTION.equals("delete")) regDelete(_Session, BRANCH); else throw newRunTimeException("Invalid ACTION attribute:" + ACTION); return cfTagReturnType.NORMAL; } // ------------------------------------------------------- /* * getType */ private int getType(cfSession _Session) throws cfmRunTimeException { String TYPE_STR = getDynamic(_Session, "TYPE").getString().toUpperCase(); if (TYPE_STR.equals("STRING")) return STRING_TYPE; if (TYPE_STR.equals("DWORD")) return DWORD_TYPE; if (TYPE_STR.equals("KEY")) return KEY_TYPE; if (TYPE_STR.equals("ANY")) return ANY_TYPE; throw newRunTimeException("the specified type is not supported - " + TYPE_STR); } /* * regGetAll */ private void regGetAll(cfSession _Session, String BRANCH) throws cfmRunTimeException { if (!containsAttribute("NAME")) throw newRunTimeException("Missing NAME attribute"); String NAME = getDynamic(_Session, "NAME").getString(); // If a SORT attribute was specified then get the sort comparator and check // the // SORT attribute for valid values regComparator sortComparator = null; if (containsAttribute("SORT")) sortComparator = getSortComparator(getDynamic(_Session, "SORT").getString()); // Get the registry entries List<Map<String, cfData>> results = regGetAll(this, BRANCH, getType(_Session), NAME); if (results == null) return; // If a SORT attribute was specified then sort the results if (sortComparator != null) Collections.sort(results, sortComparator); cfQueryResultData qT = new cfQueryResultData(new String[] { "entry", "type", "value" }, "CFREGISTRY"); qT.populateQuery(results); _Session.setData(NAME, qT); } /* * regGet */ private void regGet(cfSession _Session, String BRANCH) throws cfmRunTimeException { if (!containsAttribute("ENTRY")) throw newRunTimeException("Missing ENTRY attribute"); if (!containsAttribute("VARIABLE")) throw newRunTimeException("Missing VARIABLE attribute"); int TYPE = getType(_Session); if (TYPE == ANY_TYPE) throw newRunTimeException("A type of any is not supported with action get."); cfData data = regGet(this, BRANCH, getDynamic(_Session, "ENTRY").getString(), TYPE); if (data == null) return; _Session.setData(getDynamic(_Session, "VARIABLE").getString(), data); } /* * regSet */ private void regSet(cfSession _Session, String BRANCH) throws cfmRunTimeException { if (!containsAttribute("ENTRY")) throw newRunTimeException("Missing ENTRY attribute"); // String ENTRY = getDynamic( _Session, "ENTRY" ).getString().toUpperCase(); String VALUE = getDynamic(_Session, "VALUE").getString(); int TYPE = getType(_Session); if (TYPE == ANY_TYPE) throw newRunTimeException("A type of any is not supported with action set."); regSet(this, BRANCH, getDynamic(_Session, "ENTRY").getString(), TYPE, VALUE); } /* * regDelete */ private void regDelete(cfSession _Session, String BRANCH) throws cfmRunTimeException { if (containsAttribute("ENTRY")) regDelete(this, BRANCH, getDynamic(_Session, "ENTRY").getString()); else regDelete(this, BRANCH, null); } /* * getSortComparator */ private regComparator getSortComparator(String sortString) throws cfmRunTimeException { String sortColumns[] = new String[3]; boolean bAscending[] = new boolean[3]; List<String> tokens = string.split(sortString.toLowerCase(), ","); for (int i = 0; i < tokens.size(); i++) { String sortCol; String sortOrd; String subSort = tokens.get(i).toString().trim(); // --[ Find out the order int c1 = subSort.indexOf(" "); if (c1 == -1) { sortCol = subSort; sortOrd = "asc"; } else { sortCol = subSort.substring(0, c1).trim(); sortOrd = subSort.substring(c1 + 1); } if (!sortCol.equals("entry") && !sortCol.equals("type") && !sortCol.equals("value")) throw newRunTimeException("The sort attribute can only be used for columns Entry, Type and Value. The column '" + sortCol + "' is not valid."); boolean bAsc; if (sortOrd.equals("asc")) bAsc = true; else if (sortOrd.equals("desc")) bAsc = false; else throw newRunTimeException("The sort attribute can only sort a column as 'asc' or 'desc'. The sort order '" + sortOrd + "' is not valid."); sortColumns[i] = sortCol; bAscending[i] = bAsc; } return new regComparator(sortColumns, bAscending); } /* * regComparator */ class regComparator implements Comparator<Map<String, cfData>>, Serializable { private static final long serialVersionUID = 1L; String sortColumns[]; boolean bAscending[]; public regComparator(String[] _sortColumns, boolean[] _bAscending) { sortColumns = _sortColumns; bAscending = _bAscending; } public int compare(Map<String, cfData> o1, Map<String, cfData> o2) { return compare(o1, o2, 0); } public int compare(Map<String, cfData> o1, Map<String, cfData> o2, int index) { int result; try { String sortColumn = sortColumns[index]; cfData data1 = o1.get(sortColumn); cfData data2 = o2.get(sortColumn); String TYPE1 = o1.get("type").toString(); String TYPE2 = o2.get("type").toString(); if (sortColumn.equals("value") && TYPE1.equalsIgnoreCase("DWORD") && TYPE2.equalsIgnoreCase("DWORD")) { // If they are equal then set result to 0 if (data1.getDouble() == data2.getDouble()) result = 0; else if (bAscending[index]) result = (data1.getDouble() > data2.getDouble()) ? 1 : -1; else result = (data1.getDouble() < data2.getDouble()) ? 1 : -1; } else { if (bAscending[index]) result = data1.getString().toLowerCase().compareTo(data2.getString().toLowerCase()); else result = data2.getString().toLowerCase().compareTo(data1.getString().toLowerCase()); } // If they are equal and there are more sort columns specified then try // sorting // using the next sort column if ((result == 0) && (index + 1 < sortColumns.length) && (sortColumns[index + 1] != null)) { result = compare(o1, o2, index + 1); } } catch (Exception E) { result = 0; } return result; } } private static List<Map<String, cfData>> regGetAll(cfTag tag, String BRANCH, int TYPE, String NAME) throws cfmRunTimeException { String topKeyName; String keyName; int pos = BRANCH.indexOf("\\"); if (pos == -1) { // It's a top level key (ex. HKEY_LOCAL_MACHINE) topKeyName = BRANCH; keyName = null; } else { // It's a sub-level key (ex. HKEY_LOCAL_MACHINE\SOFTWARE) topKeyName = BRANCH.substring(0, pos); keyName = BRANCH.substring(pos + 1); } RegistryKey topKey = Registry.getTopLevelKey(topKeyName); if (topKey == null) throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "the value for BRANCH is invalid - " + BRANCH)); List<Map<String, cfData>> results = new ArrayList<Map<String, cfData>>(); RegistryKey key = null; try { if (keyName == null) { key = topKey; } else { // The openSubkey() method will return null if the key doesn't exist key = Registry.openSubkey(topKey, keyName, RegistryKey.ACCESS_READ); if (key == null) throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "the registry entry cannot be opened - " + BRANCH)); } int numValues = key.getNumberValues(); int numSubKeys = key.getNumberSubkeys(); // Add the subkeys first if ((TYPE == cfREGISTRY.KEY_TYPE) || (TYPE == cfREGISTRY.ANY_TYPE)) { for (int i = 0; i < numSubKeys; i++) { String name = key.regEnumKey(i); Map<String, cfData> rowHT = new FastMap<String, cfData>(); rowHT.put("entry", new cfStringData(name)); rowHT.put("type", new cfStringData("KEY")); rowHT.put("value", new cfStringData("")); results.add(rowHT); } } // Add the values next if (TYPE != cfREGISTRY.KEY_TYPE) { for (int i = 0; i < numValues; i++) { String name = key.regEnumValue(i); try { RegistryValue regValue = key.getValue(name); if (((TYPE == cfREGISTRY.ANY_TYPE) || (TYPE == cfREGISTRY.STRING_TYPE)) && (regValue.getType() == RegistryValue.REG_SZ)) { Map<String, cfData> rowHT = new FastMap<String, cfData>(); rowHT.put("entry", new cfStringData(name)); rowHT.put("type", new cfStringData("STRING")); rowHT.put("value", new cfStringData(((RegStringValue) regValue).getData())); results.add(rowHT); } else if (((TYPE == cfREGISTRY.ANY_TYPE) || (TYPE == cfREGISTRY.DWORD_TYPE)) && (regValue.getType() == RegistryValue.REG_DWORD)) { Map<String, cfData> rowHT = new FastMap<String, cfData>(); rowHT.put("entry", new cfStringData(name)); rowHT.put("type", new cfStringData("DWORD")); rowHT.put("value", new cfNumberData(((RegDWordValue) regValue).getData())); results.add(rowHT); } } catch (RegistryException re) { String msg = re.getMessage(); if (msg.indexOf("REG_QWORD is not supported") == -1) throw re; } } } } catch (RegistryException re) { throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "error retrieving registry information - " + re.getMessage())); } finally { // If we opened a key then make sure we close it closeKey(key); } return results; } private static cfData regGet(cfTag tag, String BRANCH, String ENTRY, int TYPE) throws cfmRunTimeException { String topKeyName; String keyName; int pos = BRANCH.indexOf("\\"); if (pos == -1) { // It's a top level key (ex. HKEY_LOCAL_MACHINE) topKeyName = BRANCH; keyName = null; } else { // It's a sub-level key (ex. HKEY_LOCAL_MACHINE\SOFTWARE) topKeyName = BRANCH.substring(0, pos); keyName = BRANCH.substring(pos + 1); } RegistryKey topKey = Registry.getTopLevelKey(topKeyName); if (topKey == null) return null; // If we fail to get the key's default value then try to return it as a // value // to match the behaviour of CFMX 7.0. if (TYPE == cfREGISTRY.KEY_TYPE) { cfData defaultValue = getKeysDefaultValue(topKey, keyName, ENTRY); if (defaultValue != null) return defaultValue; } RegistryKey key = null; try { if (keyName == null) { key = topKey; } else { // The openSubkey() method will return null if the key doesn't exist key = Registry.openSubkey(topKey, keyName, RegistryKey.ACCESS_READ); if (key == null) return null; } // The getValue() method will throw a NoSuchValueException if the value // doesn't exist RegistryValue regValue = key.getValue(ENTRY); // CFMX 7.0 seems to ignore the TYPE attribute so we will too. if (regValue.getType() == RegistryValue.REG_SZ) return new cfStringData(((RegStringValue) regValue).getData()); else return new cfNumberData(((RegDWordValue) regValue).getData()); } catch (NoSuchValueException nsve) { return null; } catch (RegistryException re) { throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "error retrieving registry information - " + re.getMessage())); } finally { // If we opened a key then make sure we close it closeKey(key); } } private static cfData getKeysDefaultValue(RegistryKey topKey, String keyName, String ENTRY) { RegistryKey key = null; try { // The openSubkey() method will return null if the key doesn't exist key = Registry.openSubkey(topKey, keyName + "\\" + ENTRY, RegistryKey.ACCESS_READ); if (key == null) return null; return new cfStringData(key.getDefaultValue()); } catch (RegistryException re) { return null; } finally { // If we opened a key then make sure we close it closeKey(key); } } public static void regSet(cfTag tag, String BRANCH, String ENTRY, int TYPE, String VALUE) throws cfmRunTimeException { String topKeyName; String keyName; int pos = BRANCH.indexOf("\\"); if (pos == -1) { // It's a top level key (ex. HKEY_LOCAL_MACHINE) topKeyName = BRANCH; keyName = null; // NOTE: this has been tested and works with HKEY_CLASSES_ROOT, // HKEY_CURRENT_USER and HKEY_CURRENT_CONFIG. // NOTE: using REGEDIT to add a key directly to HKEY_LOCAL_MACHINE or // HKEY_USERS fails so this appears to // be a Windows limitation. if ((TYPE == cfREGISTRY.KEY_TYPE) && (BRANCH.equals("HKEY_LOCAL_MACHINE") || BRANCH.equals("HKEY_USERS"))) throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "adding a key directly to HKEY_LOCAL_MACHINE or HKEY_USERS is not supported")); } else { // It's a sub-level key (ex. HKEY_LOCAL_MACHINE\SOFTWARE) topKeyName = BRANCH.substring(0, pos); keyName = BRANCH.substring(pos + 1); } RegistryKey topKey = Registry.getTopLevelKey(topKeyName); if (topKey == null) throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "the value for BRANCH is invalid - " + BRANCH)); RegistryKey key = null; try { if (keyName == null) { key = topKey; } else { // The openSubkey() method will return null if the key doesn't exist key = Registry.openSubkey(topKey, keyName, RegistryKey.ACCESS_WRITE); if (key == null) { // If the key doesn't exist then create it. Fix for bug NA#2890. key = topKey.createSubKey(keyName, "", RegistryKey.ACCESS_WRITE); } } switch (TYPE) { case cfREGISTRY.KEY_TYPE: RegistryKey subkey = key.createSubKey(ENTRY, "", RegistryKey.ACCESS_WRITE); try { subkey.closeKey(); } catch (RegistryException re) { cfEngine.log("-] ERROR - failed to close registry key: " + keyName + "\\" + ENTRY); } break; case cfREGISTRY.STRING_TYPE: RegStringValue strValue = new RegStringValue(key, ENTRY, VALUE); key.setValue(strValue); key.flushKey(); break; case cfREGISTRY.DWORD_TYPE: int value; if (VALUE.equals("")) value = 0; else value = com.nary.util.string.convertToInteger(VALUE, 0); RegDWordValue dwordValue = new RegDWordValue(key, ENTRY, RegistryValue.REG_DWORD, value); key.setValue(dwordValue); key.flushKey(); break; } } catch (RegistryException re) { throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "error setting registry entry - " + re.getMessage())); } finally { // If we opened a key then make sure we close it closeKey(key); } } private static void regDelete(cfTag tag, String BRANCH, String ENTRY) throws cfmRunTimeException { String topKeyName; String keyName; int pos = BRANCH.indexOf("\\"); if (pos == -1) { // It's a top level key (ex. HKEY_LOCAL_MACHINE) topKeyName = BRANCH; keyName = null; } else { // It's a sub-level key (ex. HKEY_LOCAL_MACHINE\SOFTWARE) topKeyName = BRANCH.substring(0, pos); keyName = BRANCH.substring(pos + 1); } RegistryKey topKey = Registry.getTopLevelKey(topKeyName); if (topKey == null) throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "the value for BRANCH is invalid - " + BRANCH)); RegistryKey key = null; try { if (ENTRY == null) { if (keyName == null) throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "cannot delete a top level key - " + BRANCH)); // We're deleting a key deleteSubKeyTree(tag, BRANCH, topKey, keyName); } else { // We're deleting a value if (keyName == null) { key = topKey; } else { // The openSubkey() method will return null if the key doesn't exist key = Registry.openSubkey(topKey, keyName, RegistryKey.ACCESS_WRITE); if (key == null) throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "error deleting registry entry. The entry does not exist - " + BRANCH)); } key.deleteValue(ENTRY); } } catch (RegistryException re) { throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "error deleting registry entry - " + re.getMessage())); } finally { // If we opened a key then make sure we close it closeKey(key); } } private static void deleteSubKeyTree(cfTag tag, String BRANCH, RegistryKey topKey, String keyName) throws cfmRunTimeException { RegistryKey subkey = null; try { // First open the subkey to see if it contains any keys // NOTE: The openSubkey() method will return null if the key doesn't exist subkey = Registry.openSubkey(topKey, keyName, RegistryKey.ACCESS_READ); if (subkey == null) throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "error deleting registry entry. The entry does not exist - " + BRANCH)); // We need to delete all of the keys contained by the subkey before it can // be deleted // NOTE: since we are deleting keys we need to start with the last key to // avoid a no // more items exception. int numSubKeys = subkey.getNumberSubkeys(); for (int i = numSubKeys - 1; i >= 0; i--) { String name = subkey.regEnumKey(i); deleteSubKeyTree(tag, BRANCH, topKey, keyName + "\\" + name); } // Now we can delete the subkey. Close it, set it to null so we don't try // to close it again in the finally block and delete it. closeKey(subkey); subkey = null; deleteSubKey(tag, BRANCH, topKey, keyName); } catch (RegistryException re) { throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "error deleting registry entry - " + re.getMessage())); } finally { // If we opened a subkey then make sure we close it closeKey(subkey); } } private static void deleteSubKey(cfTag tag, String BRANCH, RegistryKey topKey, String keyName) throws cfmRunTimeException { RegistryKey key = null; try { String subKeyName; int pos = keyName.lastIndexOf("\\"); if (pos == -1) { key = topKey; subKeyName = keyName; } else { subKeyName = keyName.substring(pos + 1); keyName = keyName.substring(0, pos); // The openSubkey() method will return null if the key doesn't exist key = Registry.openSubkey(topKey, keyName, RegistryKey.ACCESS_WRITE); if (key == null) throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "error deleting registry entry. The entry does not exist - " + BRANCH)); } key.deleteSubKey(subKeyName); } catch (RegistryException re) { throw new cfmRunTimeException(catchDataFactory.runtimeException(tag, "error deleting registry entry - " + re.getMessage())); } finally { // If we opened a key then make sure we close it closeKey(key); } } private static void closeKey(RegistryKey key) { if (key != null) { try { key.closeKey(); } catch (RegistryException re) { cfEngine.log("-] ERROR - failed to close registry key: " + re.getMessage()); } } } }