1 /*
   2  * Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.util.prefs;
  27 
  28 import java.util.Map;
  29 import java.util.TreeMap;
  30 import java.util.StringTokenizer;
  31 import java.io.ByteArrayOutputStream;
  32 import sun.util.logging.PlatformLogger;
  33 
  34 /**
  35  * Windows registry based implementation of  <tt>Preferences</tt>.
  36  * <tt>Preferences</tt>' <tt>systemRoot</tt> and <tt>userRoot</tt> are stored in
  37  * <tt>HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs</tt> and
  38  * <tt>HKEY_CURRENT_USER\Software\JavaSoft\Prefs</tt> correspondingly.
  39  *
  40  * @author  Konstantin Kladko
  41  * @see Preferences
  42  * @see PreferencesFactory
  43  * @since 1.4
  44  */
  45 
  46 class WindowsPreferences extends AbstractPreferences{
  47 
  48     /**
  49      * Logger for error messages
  50      */
  51     private static PlatformLogger logger;
  52 
  53     /**
  54      * Windows registry path to <tt>Preferences</tt>'s root nodes.
  55      */
  56     private static final byte[] WINDOWS_ROOT_PATH
  57                                = stringToByteArray("Software\\JavaSoft\\Prefs");
  58 
  59     /**
  60      * Windows handles to <tt>HKEY_CURRENT_USER</tt> and
  61      * <tt>HKEY_LOCAL_MACHINE</tt> hives.
  62      */
  63     private static final int HKEY_CURRENT_USER = 0x80000001;
  64     private static final int HKEY_LOCAL_MACHINE = 0x80000002;
  65 
  66     /**
  67      * Mount point for <tt>Preferences</tt>'  user root.
  68      */
  69     private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER;
  70 
  71     /**
  72      * Mount point for <tt>Preferences</tt>'  system root.
  73      */
  74     private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE;
  75 
  76     /**
  77      * Maximum byte-encoded path length for Windows native functions,
  78      * ending <tt>null</tt> character not included.
  79      */
  80     private static final int MAX_WINDOWS_PATH_LENGTH = 256;
  81 
  82     /**
  83      * User root node.
  84      */
  85     static final Preferences userRoot =
  86          new WindowsPreferences(USER_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);
  87 
  88     /**
  89      * System root node.
  90      */
  91     static final Preferences systemRoot =
  92         new WindowsPreferences(SYSTEM_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);
  93 
  94     /*  Windows error codes. */
  95     private static final int ERROR_SUCCESS = 0;
  96     private static final int ERROR_FILE_NOT_FOUND = 2;
  97     private static final int ERROR_ACCESS_DENIED = 5;
  98 
  99     /* Constants used to interpret returns of native functions    */
 100     private static final int NATIVE_HANDLE = 0;
 101     private static final int ERROR_CODE = 1;
 102     private static final int SUBKEYS_NUMBER = 0;
 103     private static final int VALUES_NUMBER = 2;
 104     private static final int MAX_KEY_LENGTH = 3;
 105     private static final int MAX_VALUE_NAME_LENGTH = 4;
 106     private static final int DISPOSITION = 2;
 107     private static final int REG_CREATED_NEW_KEY = 1;
 108     private static final int REG_OPENED_EXISTING_KEY = 2;
 109     private static final int NULL_NATIVE_HANDLE = 0;
 110 
 111     /* Windows security masks */
 112     private static final int DELETE = 0x10000;
 113     private static final int KEY_QUERY_VALUE = 1;
 114     private static final int KEY_SET_VALUE = 2;
 115     private static final int KEY_CREATE_SUB_KEY = 4;
 116     private static final int KEY_ENUMERATE_SUB_KEYS = 8;
 117     private static final int KEY_READ = 0x20019;
 118     private static final int KEY_WRITE = 0x20006;
 119     private static final int KEY_ALL_ACCESS = 0xf003f;
 120 
 121     /**
 122      * Initial time between registry access attempts, in ms. The time is doubled
 123      * after each failing attempt (except the first).
 124      */
 125     private static int INIT_SLEEP_TIME = 50;
 126 
 127     /**
 128      * Maximum number of registry access attempts.
 129      */
 130     private static int MAX_ATTEMPTS = 5;
 131 
 132     /**
 133      * BackingStore availability flag.
 134      */
 135     private boolean isBackingStoreAvailable = true;
 136 
 137     /**
 138      * Java wrapper for Windows registry API RegOpenKey()
 139      */
 140     private static native int[] WindowsRegOpenKey(int hKey, byte[] subKey,
 141                                                          int securityMask);
 142     /**
 143      * Retries RegOpenKey() MAX_ATTEMPTS times before giving up.
 144      */
 145     private static int[] WindowsRegOpenKey1(int hKey, byte[] subKey,
 146                                                       int securityMask) {
 147         int[] result = WindowsRegOpenKey(hKey, subKey, securityMask);
 148         if (result[ERROR_CODE] == ERROR_SUCCESS) {
 149             return result;
 150         } else if (result[ERROR_CODE] == ERROR_FILE_NOT_FOUND) {
 151             logger().warning("Trying to recreate Windows registry node " +
 152             byteArrayToString(subKey) + " at root 0x" +
 153             Integer.toHexString(hKey) + ".");
 154             // Try recreation
 155             int handle = WindowsRegCreateKeyEx(hKey, subKey)[NATIVE_HANDLE];
 156             WindowsRegCloseKey(handle);
 157             return WindowsRegOpenKey(hKey, subKey, securityMask);
 158         } else if (result[ERROR_CODE] != ERROR_ACCESS_DENIED) {
 159             long sleepTime = INIT_SLEEP_TIME;
 160             for (int i = 0; i < MAX_ATTEMPTS; i++) {
 161             try {
 162                 Thread.sleep(sleepTime);
 163             } catch(InterruptedException e) {
 164                 return result;
 165             }
 166             sleepTime *= 2;
 167             result = WindowsRegOpenKey(hKey, subKey, securityMask);
 168             if (result[ERROR_CODE] == ERROR_SUCCESS) {
 169                 return result;
 170             }
 171             }
 172         }
 173         return result;
 174     }
 175 
 176      /**
 177      * Java wrapper for Windows registry API RegCloseKey()
 178      */
 179     private static native int WindowsRegCloseKey(int hKey);
 180 
 181     /**
 182      * Java wrapper for Windows registry API RegCreateKeyEx()
 183      */
 184     private static native int[] WindowsRegCreateKeyEx(int hKey, byte[] subKey);
 185 
 186     /**
 187      * Retries RegCreateKeyEx() MAX_ATTEMPTS times before giving up.
 188      */
 189     private static int[] WindowsRegCreateKeyEx1(int hKey, byte[] subKey) {
 190         int[] result = WindowsRegCreateKeyEx(hKey, subKey);
 191         if (result[ERROR_CODE] == ERROR_SUCCESS) {
 192                 return result;
 193             } else {
 194                 long sleepTime = INIT_SLEEP_TIME;
 195                 for (int i = 0; i < MAX_ATTEMPTS; i++) {
 196                 try {
 197                     Thread.sleep(sleepTime);
 198                 } catch(InterruptedException e) {
 199                     return result;
 200                 }
 201                 sleepTime *= 2;
 202                 result = WindowsRegCreateKeyEx(hKey, subKey);
 203                 if (result[ERROR_CODE] == ERROR_SUCCESS) {
 204                 return result;
 205                 }
 206             }
 207         }
 208         return result;
 209     }
 210     /**
 211      * Java wrapper for Windows registry API RegDeleteKey()
 212      */
 213     private static native int WindowsRegDeleteKey(int hKey, byte[] subKey);
 214 
 215     /**
 216      * Java wrapper for Windows registry API RegFlushKey()
 217      */
 218     private static native int WindowsRegFlushKey(int hKey);
 219 
 220     /**
 221      * Retries RegFlushKey() MAX_ATTEMPTS times before giving up.
 222      */
 223     private static int WindowsRegFlushKey1(int hKey) {
 224         int result = WindowsRegFlushKey(hKey);
 225         if (result == ERROR_SUCCESS) {
 226                 return result;
 227             } else {
 228                 long sleepTime = INIT_SLEEP_TIME;
 229                 for (int i = 0; i < MAX_ATTEMPTS; i++) {
 230                 try {
 231                     Thread.sleep(sleepTime);
 232                 } catch(InterruptedException e) {
 233                     return result;
 234                 }
 235                 sleepTime *= 2;
 236                 result = WindowsRegFlushKey(hKey);
 237                 if (result == ERROR_SUCCESS) {
 238                 return result;
 239                 }
 240             }
 241         }
 242         return result;
 243     }
 244 
 245     /**
 246      * Java wrapper for Windows registry API RegQueryValueEx()
 247      */
 248     private static native byte[] WindowsRegQueryValueEx(int hKey,
 249                                                               byte[] valueName);
 250     /**
 251      * Java wrapper for Windows registry API RegSetValueEx()
 252      */
 253     private static native int WindowsRegSetValueEx(int hKey, byte[] valueName,
 254                                                          byte[] value);
 255     /**
 256      * Retries RegSetValueEx() MAX_ATTEMPTS times before giving up.
 257      */
 258     private static int WindowsRegSetValueEx1(int hKey, byte[] valueName,
 259                                                          byte[] value) {
 260         int result = WindowsRegSetValueEx(hKey, valueName, value);
 261         if (result == ERROR_SUCCESS) {
 262                 return result;
 263             } else {
 264                 long sleepTime = INIT_SLEEP_TIME;
 265                 for (int i = 0; i < MAX_ATTEMPTS; i++) {
 266                 try {
 267                     Thread.sleep(sleepTime);
 268                 } catch(InterruptedException e) {
 269                     return result;
 270                 }
 271                 sleepTime *= 2;
 272                 result = WindowsRegSetValueEx(hKey, valueName, value);
 273                 if (result == ERROR_SUCCESS) {
 274                 return result;
 275                 }
 276             }
 277         }
 278         return result;
 279     }
 280 
 281     /**
 282      * Java wrapper for Windows registry API RegDeleteValue()
 283      */
 284     private static native int WindowsRegDeleteValue(int hKey, byte[] valueName);
 285 
 286     /**
 287      * Java wrapper for Windows registry API RegQueryInfoKey()
 288      */
 289     private static native int[] WindowsRegQueryInfoKey(int hKey);
 290 
 291     /**
 292      * Retries RegQueryInfoKey() MAX_ATTEMPTS times before giving up.
 293      */
 294     private static int[] WindowsRegQueryInfoKey1(int hKey) {
 295         int[] result = WindowsRegQueryInfoKey(hKey);
 296         if (result[ERROR_CODE] == ERROR_SUCCESS) {
 297                 return result;
 298             } else {
 299                 long sleepTime = INIT_SLEEP_TIME;
 300                 for (int i = 0; i < MAX_ATTEMPTS; i++) {
 301                 try {
 302                     Thread.sleep(sleepTime);
 303                 } catch(InterruptedException e) {
 304                     return result;
 305                 }
 306                 sleepTime *= 2;
 307                 result = WindowsRegQueryInfoKey(hKey);
 308                 if (result[ERROR_CODE] == ERROR_SUCCESS) {
 309                 return result;
 310                 }
 311             }
 312         }
 313         return result;
 314     }
 315 
 316     /**
 317      * Java wrapper for Windows registry API RegEnumKeyEx()
 318      */
 319     private static native byte[] WindowsRegEnumKeyEx(int hKey, int subKeyIndex,
 320                                       int maxKeyLength);
 321 
 322     /**
 323      * Retries RegEnumKeyEx() MAX_ATTEMPTS times before giving up.
 324      */
 325     private static byte[] WindowsRegEnumKeyEx1(int hKey, int subKeyIndex,
 326                                       int maxKeyLength) {
 327         byte[] result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);
 328         if (result != null) {
 329                 return result;
 330             } else {
 331                 long sleepTime = INIT_SLEEP_TIME;
 332                 for (int i = 0; i < MAX_ATTEMPTS; i++) {
 333                 try {
 334                     Thread.sleep(sleepTime);
 335                 } catch(InterruptedException e) {
 336                     return result;
 337                 }
 338                 sleepTime *= 2;
 339                 result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);
 340                 if (result != null) {
 341                 return result;
 342                 }
 343             }
 344         }
 345         return result;
 346     }
 347 
 348     /**
 349      * Java wrapper for Windows registry API RegEnumValue()
 350      */
 351     private static native byte[] WindowsRegEnumValue(int hKey, int valueIndex,
 352                                       int maxValueNameLength);
 353     /**
 354      * Retries RegEnumValueEx() MAX_ATTEMPTS times before giving up.
 355      */
 356     private static byte[] WindowsRegEnumValue1(int hKey, int valueIndex,
 357                                       int maxValueNameLength) {
 358         byte[] result = WindowsRegEnumValue(hKey, valueIndex,
 359                                                             maxValueNameLength);
 360         if (result != null) {
 361                 return result;
 362             } else {
 363                 long sleepTime = INIT_SLEEP_TIME;
 364                 for (int i = 0; i < MAX_ATTEMPTS; i++) {
 365                 try {
 366                     Thread.sleep(sleepTime);
 367                 } catch(InterruptedException e) {
 368                     return result;
 369                 }
 370                 sleepTime *= 2;
 371                 result = WindowsRegEnumValue(hKey, valueIndex,
 372                                                             maxValueNameLength);
 373                 if (result != null) {
 374                 return result;
 375                 }
 376             }
 377         }
 378         return result;
 379     }
 380 
 381     /**
 382      * Constructs a <tt>WindowsPreferences</tt> node, creating underlying
 383      * Windows registry node and all its Windows parents, if they are not yet
 384      * created.
 385      * Logs a warning message, if Windows Registry is unavailable.
 386      */
 387     private WindowsPreferences(WindowsPreferences parent, String name) {
 388         super(parent, name);
 389         int parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ);
 390         if (parentNativeHandle == NULL_NATIVE_HANDLE) {
 391             // if here, openKey failed and logged
 392             isBackingStoreAvailable = false;
 393             return;
 394         }
 395         int[] result =
 396                WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name));
 397         if (result[ERROR_CODE] != ERROR_SUCCESS) {
 398             logger().warning("Could not create windows registry "
 399             + "node " + byteArrayToString(windowsAbsolutePath()) +
 400             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 401             ". Windows RegCreateKeyEx(...) returned error code " +
 402             result[ERROR_CODE] + ".");
 403             isBackingStoreAvailable = false;
 404             return;
 405         }
 406         newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
 407         closeKey(parentNativeHandle);
 408         closeKey(result[NATIVE_HANDLE]);
 409     }
 410 
 411     /**
 412      * Constructs a root node creating the underlying
 413      * Windows registry node and all of its parents, if they have not yet been
 414      * created.
 415      * Logs a warning message, if Windows Registry is unavailable.
 416      * @param rootNativeHandle Native handle to one of Windows top level keys.
 417      * @param rootDirectory Path to root directory, as a byte-encoded string.
 418      */
 419     private  WindowsPreferences(int rootNativeHandle, byte[] rootDirectory) {
 420         super(null,"");
 421         int[] result =
 422                 WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory);
 423         if (result[ERROR_CODE] != ERROR_SUCCESS) {
 424             logger().warning("Could not open/create prefs root node " +
 425             byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
 426             Integer.toHexString(rootNativeHandle()) +
 427             ". Windows RegCreateKeyEx(...) returned error code " +
 428             result[ERROR_CODE] + ".");
 429             isBackingStoreAvailable = false;
 430             return;
 431         }
 432         // Check if a new node
 433         newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
 434         closeKey(result[NATIVE_HANDLE]);
 435     }
 436 
 437     /**
 438      * Returns Windows absolute path of the current node as a byte array.
 439      * Java "/" separator is transformed into Windows "\".
 440      * @see Preferences#absolutePath()
 441      */
 442     private byte[] windowsAbsolutePath() {
 443         ByteArrayOutputStream bstream = new ByteArrayOutputStream();
 444         bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1);
 445         StringTokenizer tokenizer = new StringTokenizer(absolutePath(),"/");
 446         while (tokenizer.hasMoreTokens()) {
 447             bstream.write((byte)'\\');
 448             String nextName = tokenizer.nextToken();
 449             byte[] windowsNextName = toWindowsName(nextName);
 450             bstream.write(windowsNextName, 0, windowsNextName.length-1);
 451         }
 452         bstream.write(0);
 453         return bstream.toByteArray();
 454     }
 455 
 456     /**
 457      * Opens current node's underlying Windows registry key using a
 458      * given security mask.
 459      * @param securityMask Windows security mask.
 460      * @return Windows registry key's handle.
 461      * @see #openKey(byte[], int)
 462      * @see #openKey(int, byte[], int)
 463      * @see #closeKey(int)
 464      */
 465     private int openKey(int securityMask) {
 466         return openKey(securityMask, securityMask);
 467     }
 468 
 469     /**
 470      * Opens current node's underlying Windows registry key using a
 471      * given security mask.
 472      * @param mask1 Preferred Windows security mask.
 473      * @param mask2 Alternate Windows security mask.
 474      * @return Windows registry key's handle.
 475      * @see #openKey(byte[], int)
 476      * @see #openKey(int, byte[], int)
 477      * @see #closeKey(int)
 478      */
 479     private int openKey(int mask1, int mask2) {
 480         return openKey(windowsAbsolutePath(), mask1,  mask2);
 481     }
 482 
 483      /**
 484      * Opens Windows registry key at a given absolute path using a given
 485      * security mask.
 486      * @param windowsAbsolutePath Windows absolute path of the
 487      *        key as a byte-encoded string.
 488      * @param mask1 Preferred Windows security mask.
 489      * @param mask2 Alternate Windows security mask.
 490      * @return Windows registry key's handle.
 491      * @see #openKey(int)
 492      * @see #openKey(int, byte[],int)
 493      * @see #closeKey(int)
 494      */
 495     private int openKey(byte[] windowsAbsolutePath, int mask1, int mask2) {
 496         /*  Check if key's path is short enough be opened at once
 497             otherwise use a path-splitting procedure */
 498         if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) {
 499              int[] result = WindowsRegOpenKey1(rootNativeHandle(),
 500                                                windowsAbsolutePath, mask1);
 501              if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
 502                  result = WindowsRegOpenKey1(rootNativeHandle(),
 503                                              windowsAbsolutePath, mask2);
 504 
 505              if (result[ERROR_CODE] != ERROR_SUCCESS) {
 506                 logger().warning("Could not open windows "
 507                 + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 508                 " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 509                 ". Windows RegOpenKey(...) returned error code " +
 510                 result[ERROR_CODE] + ".");
 511                 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
 512                 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) {
 513                     throw new SecurityException("Could not open windows "
 514                 + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 515                 " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 516                 ": Access denied");
 517                 }
 518              }
 519              return result[NATIVE_HANDLE];
 520         } else {
 521             return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2);
 522         }
 523     }
 524 
 525      /**
 526      * Opens Windows registry key at a given relative path
 527      * with respect to a given Windows registry key.
 528      * @param windowsAbsolutePath Windows relative path of the
 529      *        key as a byte-encoded string.
 530      * @param nativeHandle handle to the base Windows key.
 531      * @param mask1 Preferred Windows security mask.
 532      * @param mask2 Alternate Windows security mask.
 533      * @return Windows registry key's handle.
 534      * @see #openKey(int)
 535      * @see #openKey(byte[],int)
 536      * @see #closeKey(int)
 537      */
 538     private int openKey(int nativeHandle, byte[] windowsRelativePath,
 539                         int mask1, int mask2) {
 540     /* If the path is short enough open at once. Otherwise split the path */
 541         if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) {
 542              int[] result = WindowsRegOpenKey1(nativeHandle,
 543                                                windowsRelativePath, mask1);
 544              if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
 545                  result = WindowsRegOpenKey1(nativeHandle,
 546                                              windowsRelativePath, mask2);
 547 
 548              if (result[ERROR_CODE] != ERROR_SUCCESS) {
 549                 logger().warning("Could not open windows "
 550                 + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 551                 " at root 0x" + Integer.toHexString(nativeHandle) +
 552                 ". Windows RegOpenKey(...) returned error code " +
 553                 result[ERROR_CODE] + ".");
 554                 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
 555              }
 556              return result[NATIVE_HANDLE];
 557         } else {
 558             int separatorPosition = -1;
 559             // Be greedy - open the longest possible path
 560             for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) {
 561                 if (windowsRelativePath[i] == ((byte)'\\')) {
 562                     separatorPosition = i;
 563                     break;
 564                 }
 565             }
 566             // Split the path and do the recursion
 567             byte[] nextRelativeRoot = new byte[separatorPosition+1];
 568             System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0,
 569                                                       separatorPosition);
 570             nextRelativeRoot[separatorPosition] = 0;
 571             byte[] nextRelativePath = new byte[windowsRelativePath.length -
 572                                       separatorPosition - 1];
 573             System.arraycopy(windowsRelativePath, separatorPosition+1,
 574                              nextRelativePath, 0, nextRelativePath.length);
 575             int nextNativeHandle = openKey(nativeHandle, nextRelativeRoot,
 576                                            mask1, mask2);
 577             if (nextNativeHandle == NULL_NATIVE_HANDLE) {
 578                 return NULL_NATIVE_HANDLE;
 579             }
 580             int result = openKey(nextNativeHandle, nextRelativePath,
 581                                  mask1,mask2);
 582             closeKey(nextNativeHandle);
 583             return result;
 584         }
 585     }
 586 
 587      /**
 588      * Closes Windows registry key.
 589      * Logs a warning if Windows registry is unavailable.
 590      * @param key's Windows registry handle.
 591      * @see #openKey(int)
 592      * @see #openKey(byte[],int)
 593      * @see #openKey(int, byte[],int)
 594     */
 595     private void closeKey(int nativeHandle) {
 596         int result = WindowsRegCloseKey(nativeHandle);
 597         if (result != ERROR_SUCCESS) {
 598             logger().warning("Could not close windows "
 599             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 600             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 601             ". Windows RegCloseKey(...) returned error code " + result + ".");
 602         }
 603     }
 604 
 605      /**
 606      * Implements <tt>AbstractPreferences</tt> <tt>putSpi()</tt> method.
 607      * Puts name-value pair into the underlying Windows registry node.
 608      * Logs a warning, if Windows registry is unavailable.
 609      * @see #getSpi(String)
 610      */
 611     protected void putSpi(String javaName, String value) {
 612     int nativeHandle = openKey(KEY_SET_VALUE);
 613     if (nativeHandle == NULL_NATIVE_HANDLE) {
 614         isBackingStoreAvailable = false;
 615         return;
 616     }
 617     int result =  WindowsRegSetValueEx1(nativeHandle,
 618                           toWindowsName(javaName), toWindowsValueString(value));
 619     if (result != ERROR_SUCCESS) {
 620         logger().warning("Could not assign value to key " +
 621         byteArrayToString(toWindowsName(javaName))+ " at Windows registry node "
 622        + byteArrayToString(windowsAbsolutePath()) + " at root 0x"
 623        + Integer.toHexString(rootNativeHandle()) +
 624        ". Windows RegSetValueEx(...) returned error code " + result + ".");
 625         isBackingStoreAvailable = false;
 626         }
 627     closeKey(nativeHandle);
 628     }
 629 
 630     /**
 631      * Implements <tt>AbstractPreferences</tt> <tt>getSpi()</tt> method.
 632      * Gets a string value from the underlying Windows registry node.
 633      * Logs a warning, if Windows registry is unavailable.
 634      * @see #putSpi(String, String)
 635      */
 636     protected String getSpi(String javaName) {
 637     int nativeHandle = openKey(KEY_QUERY_VALUE);
 638     if (nativeHandle == NULL_NATIVE_HANDLE) {
 639         return null;
 640     }
 641     Object resultObject =  WindowsRegQueryValueEx(nativeHandle,
 642                                                   toWindowsName(javaName));
 643     if (resultObject == null) {
 644         closeKey(nativeHandle);
 645         return null;
 646     }
 647     closeKey(nativeHandle);
 648     return toJavaValueString((byte[]) resultObject);
 649     }
 650 
 651     /**
 652      * Implements <tt>AbstractPreferences</tt> <tt>removeSpi()</tt> method.
 653      * Deletes a string name-value pair from the underlying Windows registry
 654      * node, if this value still exists.
 655      * Logs a warning, if Windows registry is unavailable or key has already
 656      * been deleted.
 657      */
 658     protected void removeSpi(String key) {
 659         int nativeHandle = openKey(KEY_SET_VALUE);
 660         if (nativeHandle == NULL_NATIVE_HANDLE) {
 661         return;
 662         }
 663         int result =
 664             WindowsRegDeleteValue(nativeHandle, toWindowsName(key));
 665         if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
 666             logger().warning("Could not delete windows registry "
 667             + "value " + byteArrayToString(windowsAbsolutePath())+ "\\" +
 668             toWindowsName(key) + " at root 0x" +
 669             Integer.toHexString(rootNativeHandle()) +
 670             ". Windows RegDeleteValue(...) returned error code " +
 671             result + ".");
 672             isBackingStoreAvailable = false;
 673         }
 674         closeKey(nativeHandle);
 675     }
 676 
 677     /**
 678      * Implements <tt>AbstractPreferences</tt> <tt>keysSpi()</tt> method.
 679      * Gets value names from the underlying Windows registry node.
 680      * Throws a BackingStoreException and logs a warning, if
 681      * Windows registry is unavailable.
 682      */
 683     protected String[] keysSpi() throws BackingStoreException{
 684         // Find out the number of values
 685         int nativeHandle = openKey(KEY_QUERY_VALUE);
 686         if (nativeHandle == NULL_NATIVE_HANDLE) {
 687             throw new BackingStoreException("Could not open windows"
 688             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 689             " at root 0x" + Integer.toHexString(rootNativeHandle()) + ".");
 690         }
 691         int[] result =  WindowsRegQueryInfoKey1(nativeHandle);
 692         if (result[ERROR_CODE] != ERROR_SUCCESS) {
 693             String info = "Could not query windows"
 694             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 695             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 696             ". Windows RegQueryInfoKeyEx(...) returned error code " +
 697             result[ERROR_CODE] + ".";
 698             logger().warning(info);
 699             throw new BackingStoreException(info);
 700         }
 701         int maxValueNameLength = result[MAX_VALUE_NAME_LENGTH];
 702         int valuesNumber = result[VALUES_NUMBER];
 703         if (valuesNumber == 0) {
 704             closeKey(nativeHandle);
 705             return new String[0];
 706        }
 707        // Get the values
 708        String[] valueNames = new String[valuesNumber];
 709        for (int i = 0; i < valuesNumber; i++) {
 710             byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i,
 711                                                         maxValueNameLength+1);
 712             if (windowsName == null) {
 713                 String info =
 714                 "Could not enumerate value #" + i + "  of windows node " +
 715                 byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
 716                 Integer.toHexString(rootNativeHandle()) + ".";
 717                 logger().warning(info);
 718                 throw new BackingStoreException(info);
 719             }
 720             valueNames[i] = toJavaName(windowsName);
 721         }
 722         closeKey(nativeHandle);
 723         return valueNames;
 724     }
 725 
 726     /**
 727      * Implements <tt>AbstractPreferences</tt> <tt>childrenNamesSpi()</tt> method.
 728      * Calls Windows registry to retrive children of this node.
 729      * Throws a BackingStoreException and logs a warning message,
 730      * if Windows registry is not available.
 731      */
 732     protected String[] childrenNamesSpi() throws BackingStoreException {
 733         // Open key
 734         int nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS| KEY_QUERY_VALUE);
 735         if (nativeHandle == NULL_NATIVE_HANDLE) {
 736             throw new BackingStoreException("Could not open windows"
 737             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 738             " at root 0x" + Integer.toHexString(rootNativeHandle()) + ".");
 739         }
 740         // Get number of children
 741         int[] result =  WindowsRegQueryInfoKey1(nativeHandle);
 742         if (result[ERROR_CODE] != ERROR_SUCCESS) {
 743             String info = "Could not query windows"
 744             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 745             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 746             ". Windows RegQueryInfoKeyEx(...) returned error code " +
 747             result[ERROR_CODE] + ".";
 748             logger().warning(info);
 749             throw new BackingStoreException(info);
 750         }
 751         int maxKeyLength = result[MAX_KEY_LENGTH];
 752         int subKeysNumber = result[SUBKEYS_NUMBER];
 753         if (subKeysNumber == 0) {
 754             closeKey(nativeHandle);
 755             return new String[0];
 756         }
 757         String[] subkeys = new String[subKeysNumber];
 758         String[] children = new String[subKeysNumber];
 759         // Get children
 760         for (int i = 0; i < subKeysNumber; i++) {
 761             byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i,
 762                                                                 maxKeyLength+1);
 763             if (windowsName == null) {
 764                 String info =
 765                 "Could not enumerate key #" + i + "  of windows node " +
 766                 byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
 767                 Integer.toHexString(rootNativeHandle()) + ". ";
 768                 logger().warning(info);
 769                 throw new BackingStoreException(info);
 770             }
 771             String javaName = toJavaName(windowsName);
 772             children[i] = javaName;
 773         }
 774         closeKey(nativeHandle);
 775         return children;
 776     }
 777 
 778     /**
 779      * Implements <tt>Preferences</tt> <tt>flush()</tt> method.
 780      * Flushes Windows registry changes to disk.
 781      * Throws a BackingStoreException and logs a warning message if Windows
 782      * registry is not available.
 783      */
 784     public void flush() throws BackingStoreException{
 785 
 786         if (isRemoved()) {
 787             parent.flush();
 788             return;
 789         }
 790         if (!isBackingStoreAvailable) {
 791             throw new BackingStoreException(
 792                                        "flush(): Backing store not available.");
 793         }
 794         int nativeHandle = openKey(KEY_READ);
 795         if (nativeHandle == NULL_NATIVE_HANDLE) {
 796             throw new BackingStoreException("Could not open windows"
 797             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 798             " at root 0x" + Integer.toHexString(rootNativeHandle()) + ".");
 799         }
 800         int result = WindowsRegFlushKey1(nativeHandle);
 801         if (result != ERROR_SUCCESS) {
 802             String info = "Could not flush windows "
 803             + "registry node " + byteArrayToString(windowsAbsolutePath())
 804             + " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 805             ". Windows RegFlushKey(...) returned error code " + result + ".";
 806             logger().warning(info);
 807             throw new BackingStoreException(info);
 808         }
 809         closeKey(nativeHandle);
 810     }
 811 
 812 
 813     /**
 814      * Implements <tt>Preferences</tt> <tt>sync()</tt> method.
 815      * Flushes Windows registry changes to disk. Equivalent to flush().
 816      * @see flush()
 817      */
 818     public void sync() throws BackingStoreException{
 819         if (isRemoved())
 820             throw new IllegalStateException("Node has been removed");
 821         flush();
 822     }
 823 
 824     /**
 825      * Implements <tt>AbstractPreferences</tt> <tt>childSpi()</tt> method.
 826      * Constructs a child node with a
 827      * given name and creates its underlying Windows registry node,
 828      * if it does not exist.
 829      * Logs a warning message, if Windows Registry is unavailable.
 830      */
 831     protected AbstractPreferences childSpi(String name) {
 832             return new WindowsPreferences(this, name);
 833     }
 834 
 835     /**
 836      * Implements <tt>AbstractPreferences</tt> <tt>removeNodeSpi()</tt> method.
 837      * Deletes underlying Windows registry node.
 838      * Throws a BackingStoreException and logs a warning, if Windows registry
 839      * is not available.
 840      */
 841     public void removeNodeSpi() throws BackingStoreException {
 842         int parentNativeHandle =
 843                          ((WindowsPreferences)parent()).openKey(DELETE);
 844         if (parentNativeHandle == NULL_NATIVE_HANDLE) {
 845             throw new BackingStoreException("Could not open parent windows"
 846             + "registry node of " + byteArrayToString(windowsAbsolutePath()) +
 847             " at root 0x" + Integer.toHexString(rootNativeHandle()) + ".");
 848         }
 849         int result =
 850                 WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name()));
 851         if (result != ERROR_SUCCESS) {
 852             String info = "Could not delete windows "
 853             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 854             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 855             ". Windows RegDeleteKeyEx(...) returned error code " +
 856             result + ".";
 857             logger().warning(info);
 858             throw new BackingStoreException(info);
 859         }
 860         closeKey(parentNativeHandle);
 861     }
 862 
 863     /**
 864      * Converts value's or node's name from its byte array representation to
 865      * java string. Two encodings, simple and altBase64 are used. See
 866      * {@link #toWindowsName(String) toWindowsName()} for a detailed
 867      * description of encoding conventions.
 868      * @param windowsNameArray Null-terminated byte array.
 869      */
 870     private static String toJavaName(byte[] windowsNameArray) {
 871         String windowsName = byteArrayToString(windowsNameArray);
 872         // check if Alt64
 873         if ((windowsName.length()>1) &&
 874                                    (windowsName.substring(0,2).equals("/!"))) {
 875             return toJavaAlt64Name(windowsName);
 876         }
 877         StringBuffer javaName = new StringBuffer();
 878         char ch;
 879         // Decode from simple encoding
 880         for (int i = 0; i < windowsName.length(); i++){
 881             if ((ch = windowsName.charAt(i)) == '/') {
 882                 char next = ' ';
 883                 if ((windowsName.length() > i + 1) &&
 884                    ((next = windowsName.charAt(i+1)) >= 'A') && (next <= 'Z')) {
 885                 ch = next;
 886                 i++;
 887                 } else  if ((windowsName.length() > i + 1) && (next == '/')) {
 888                 ch = '\\';
 889                 i++;
 890                 }
 891             } else if (ch == '\\') {
 892                 ch = '/';
 893             }
 894             javaName.append(ch);
 895         }
 896         return javaName.toString();
 897     }
 898 
 899     /**
 900      * Converts value's or node's name from its Windows representation to java
 901      * string, using altBase64 encoding. See
 902      * {@link #toWindowsName(String) toWindowsName()} for a detailed
 903      * description of encoding conventions.
 904      */
 905 
 906     private static String toJavaAlt64Name(String windowsName) {
 907         byte[] byteBuffer =
 908                           Base64.altBase64ToByteArray(windowsName.substring(2));
 909         StringBuffer result = new StringBuffer();
 910         for (int i = 0; i < byteBuffer.length; i++) {
 911             int firstbyte = (byteBuffer[i++] & 0xff);
 912             int secondbyte =  (byteBuffer[i] & 0xff);
 913             result.append((char)((firstbyte << 8) + secondbyte));
 914         }
 915         return result.toString();
 916     }
 917 
 918     /**
 919      * Converts value's or node's name to its Windows representation
 920      * as a byte-encoded string.
 921      * Two encodings, simple and altBase64 are used.
 922      * <p>
 923      * <i>Simple</i> encoding is used, if java string does not contain
 924      * any characters less, than 0x0020, or greater, than 0x007f.
 925      * Simple encoding adds "/" character to capital letters, i.e.
 926      * "A" is encoded as "/A". Character '\' is encoded as '//',
 927      * '/' is encoded as '\'.
 928      * The constructed string is converted to byte array by truncating the
 929      * highest byte and adding the terminating <tt>null</tt> character.
 930      * <p>
 931      * <i>altBase64</i>  encoding is used, if java string does contain at least
 932      * one character less, than 0x0020, or greater, than 0x007f.
 933      * This encoding is marked by setting first two bytes of the
 934      * Windows string to '/!'. The java name is then encoded using
 935      * byteArrayToAltBase64() method from
 936      * Base64 class.
 937      */
 938     private static byte[] toWindowsName(String javaName) {
 939         StringBuffer windowsName = new StringBuffer();
 940         for (int i = 0; i < javaName.length(); i++) {
 941             char ch =javaName.charAt(i);
 942             if ((ch < 0x0020)||(ch > 0x007f)) {
 943                 // If a non-trivial character encountered, use altBase64
 944                 return toWindowsAlt64Name(javaName);
 945             }
 946             if (ch == '\\') {
 947                 windowsName.append("//");
 948             } else if (ch == '/') {
 949                 windowsName.append('\\');
 950             } else if ((ch >= 'A') && (ch <='Z')) {
 951                 windowsName.append("/" + ch);
 952             } else {
 953                 windowsName.append(ch);
 954             }
 955         }
 956         return stringToByteArray(windowsName.toString());
 957     }
 958 
 959     /**
 960      * Converts value's or node's name to its Windows representation
 961      * as a byte-encoded string, using altBase64 encoding. See
 962      * {@link #toWindowsName(String) toWindowsName()} for a detailed
 963      * description of encoding conventions.
 964      */
 965     private static byte[] toWindowsAlt64Name(String javaName) {
 966         byte[] javaNameArray = new byte[2*javaName.length()];
 967         // Convert to byte pairs
 968         int counter = 0;
 969         for (int i = 0; i < javaName.length();i++) {
 970                 int ch = javaName.charAt(i);
 971                 javaNameArray[counter++] = (byte)(ch >>> 8);
 972                 javaNameArray[counter++] = (byte)ch;
 973         }
 974 
 975         return stringToByteArray(
 976                            "/!" + Base64.byteArrayToAltBase64(javaNameArray));
 977     }
 978 
 979     /**
 980      * Converts value string from its Windows representation
 981      * to java string.  See
 982      * {@link #toWindowsValueString(String) toWindowsValueString()} for the
 983      * description of the encoding algorithm.
 984      */
 985      private static String toJavaValueString(byte[] windowsNameArray) {
 986         // Use modified native2ascii algorithm
 987         String windowsName = byteArrayToString(windowsNameArray);
 988         StringBuffer javaName = new StringBuffer();
 989         char ch;
 990         for (int i = 0; i < windowsName.length(); i++){
 991             if ((ch = windowsName.charAt(i)) == '/') {
 992                 char next = ' ';
 993 
 994                 if (windowsName.length() > i + 1 &&
 995                                     (next = windowsName.charAt(i + 1)) == 'u') {
 996                     if (windowsName.length() < i + 6){
 997                         break;
 998                     } else {
 999                         ch = (char)Integer.parseInt
1000                                       (windowsName.substring(i + 2, i + 6), 16);
1001                         i += 5;
1002                     }
1003                 } else
1004                 if ((windowsName.length() > i + 1) &&
1005                           ((windowsName.charAt(i+1)) >= 'A') && (next <= 'Z')) {
1006                 ch = next;
1007                 i++;
1008                 } else  if ((windowsName.length() > i + 1) &&
1009                                                (next == '/')) {
1010                 ch = '\\';
1011                 i++;
1012                 }
1013             } else if (ch == '\\') {
1014                 ch = '/';
1015             }
1016             javaName.append(ch);
1017         }
1018         return javaName.toString();
1019     }
1020 
1021     /**
1022      * Converts value string to it Windows representation.
1023      * as a byte-encoded string.
1024      * Encoding algorithm adds "/" character to capital letters, i.e.
1025      * "A" is encoded as "/A". Character '\' is encoded as '//',
1026      * '/' is encoded as  '\'.
1027      * Then encoding scheme similar to jdk's native2ascii converter is used
1028      * to convert java string to a byte array of ASCII characters.
1029      */
1030     private static byte[] toWindowsValueString(String javaName) {
1031         StringBuffer windowsName = new StringBuffer();
1032         for (int i = 0; i < javaName.length(); i++) {
1033             char ch =javaName.charAt(i);
1034             if ((ch < 0x0020)||(ch > 0x007f)){
1035                 // write \udddd
1036                 windowsName.append("/u");
1037                 String hex = Integer.toHexString(javaName.charAt(i));
1038                 StringBuffer hex4 = new StringBuffer(hex);
1039                 hex4.reverse();
1040                 int len = 4 - hex4.length();
1041                 for (int j = 0; j < len; j++){
1042                     hex4.append('0');
1043                 }
1044                 for (int j = 0; j < 4; j++){
1045                     windowsName.append(hex4.charAt(3 - j));
1046                 }
1047             } else if (ch == '\\') {
1048                 windowsName.append("//");
1049             } else if (ch == '/') {
1050                 windowsName.append('\\');
1051             } else if ((ch >= 'A') && (ch <='Z')) {
1052                 windowsName.append("/" + ch);
1053             } else {
1054                 windowsName.append(ch);
1055             }
1056         }
1057         return stringToByteArray(windowsName.toString());
1058     }
1059 
1060     /**
1061      * Returns native handle for the top Windows node for this node.
1062      */
1063     private int rootNativeHandle() {
1064         return (isUserNode()? USER_ROOT_NATIVE_HANDLE :
1065                               SYSTEM_ROOT_NATIVE_HANDLE);
1066     }
1067 
1068     /**
1069      * Returns this java string as a null-terminated byte array
1070      */
1071     private static byte[] stringToByteArray(String str) {
1072         byte[] result = new byte[str.length()+1];
1073         for (int i = 0; i < str.length(); i++) {
1074             result[i] = (byte) str.charAt(i);
1075         }
1076         result[str.length()] = 0;
1077         return result;
1078     }
1079 
1080     /**
1081      * Converts a null-terminated byte array to java string
1082      */
1083     private static String byteArrayToString(byte[] array) {
1084         StringBuffer result = new StringBuffer();
1085         for (int i = 0; i < array.length - 1; i++) {
1086             result.append((char)array[i]);
1087         }
1088         return result.toString();
1089     }
1090 
1091    /**
1092     * Empty, never used implementation  of AbstractPreferences.flushSpi().
1093     */
1094     protected void flushSpi() throws BackingStoreException {
1095         // assert false;
1096     }
1097 
1098    /**
1099     * Empty, never used implementation  of AbstractPreferences.flushSpi().
1100     */
1101     protected void syncSpi() throws BackingStoreException {
1102         // assert false;
1103     }
1104 
1105     private static synchronized PlatformLogger logger() {
1106         if (logger == null) {
1107             logger = PlatformLogger.getLogger("java.util.prefs");
1108         }
1109         return logger;
1110     }
1111 }