45 */ 46 47 class WindowsPreferences extends AbstractPreferences { 48 49 static { 50 PrivilegedAction<Void> load = () -> { 51 System.loadLibrary("prefs"); 52 return null; 53 }; 54 AccessController.doPrivileged(load); 55 } 56 57 /** 58 * Logger for error messages 59 */ 60 private static PlatformLogger logger; 61 62 /** 63 * Windows registry path to <tt>Preferences</tt>'s root nodes. 64 */ 65 private static final byte[] WINDOWS_ROOT_PATH 66 = stringToByteArray("Software\\JavaSoft\\Prefs"); 67 68 /** 69 * Windows handles to <tt>HKEY_CURRENT_USER</tt> and 70 * <tt>HKEY_LOCAL_MACHINE</tt> hives. 71 */ 72 private static final int HKEY_CURRENT_USER = 0x80000001; 73 private static final int HKEY_LOCAL_MACHINE = 0x80000002; 74 75 /** 76 * Mount point for <tt>Preferences</tt>' user root. 77 */ 78 private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER; 79 80 /** 81 * Mount point for <tt>Preferences</tt>' system root. 82 */ 83 private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE; 84 85 /** 86 * Maximum byte-encoded path length for Windows native functions, 387 return result; 388 } 389 390 /** 391 * Constructs a <tt>WindowsPreferences</tt> node, creating underlying 392 * Windows registry node and all its Windows parents, if they are not yet 393 * created. 394 * Logs a warning message, if Windows Registry is unavailable. 395 */ 396 private WindowsPreferences(WindowsPreferences parent, String name) { 397 super(parent, name); 398 int parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ); 399 if (parentNativeHandle == NULL_NATIVE_HANDLE) { 400 // if here, openKey failed and logged 401 isBackingStoreAvailable = false; 402 return; 403 } 404 int[] result = 405 WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name)); 406 if (result[ERROR_CODE] != ERROR_SUCCESS) { 407 logger().warning("Could not create windows registry " 408 + "node " + byteArrayToString(windowsAbsolutePath()) + 409 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 410 ". Windows RegCreateKeyEx(...) returned error code " + 411 result[ERROR_CODE] + "."); 412 isBackingStoreAvailable = false; 413 return; 414 } 415 newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY); 416 closeKey(parentNativeHandle); 417 closeKey(result[NATIVE_HANDLE]); 418 } 419 420 /** 421 * Constructs a root node creating the underlying 422 * Windows registry node and all of its parents, if they have not yet been 423 * created. 424 * Logs a warning message, if Windows Registry is unavailable. 425 * @param rootNativeHandle Native handle to one of Windows top level keys. 426 * @param rootDirectory Path to root directory, as a byte-encoded string. 427 */ 428 private WindowsPreferences(int rootNativeHandle, byte[] rootDirectory) { 429 super(null,""); 430 int[] result = 431 WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory); 432 if (result[ERROR_CODE] != ERROR_SUCCESS) { 433 logger().warning("Could not open/create prefs root node " + 434 byteArrayToString(windowsAbsolutePath()) + " at root 0x" + 435 Integer.toHexString(rootNativeHandle()) + 436 ". Windows RegCreateKeyEx(...) returned error code " + 437 result[ERROR_CODE] + "."); 438 isBackingStoreAvailable = false; 439 return; 440 } 441 // Check if a new node 442 newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY); 443 closeKey(result[NATIVE_HANDLE]); 444 } 445 446 /** 447 * Returns Windows absolute path of the current node as a byte array. 448 * Java "/" separator is transformed into Windows "\". 449 * @see Preferences#absolutePath() 450 */ 451 private byte[] windowsAbsolutePath() { 452 ByteArrayOutputStream bstream = new ByteArrayOutputStream(); 453 bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1); 454 StringTokenizer tokenizer = new StringTokenizer(absolutePath(),"/"); 455 while (tokenizer.hasMoreTokens()) { 456 bstream.write((byte)'\\'); 457 String nextName = tokenizer.nextToken(); 458 byte[] windowsNextName = toWindowsName(nextName); 459 bstream.write(windowsNextName, 0, windowsNextName.length-1); 460 } 461 bstream.write(0); 462 return bstream.toByteArray(); 463 } 464 465 /** 466 * Opens current node's underlying Windows registry key using a 467 * given security mask. 468 * @param securityMask Windows security mask. 469 * @return Windows registry key's handle. 470 * @see #openKey(byte[], int) 471 * @see #openKey(int, byte[], int) 472 * @see #closeKey(int) 473 */ 474 private int openKey(int securityMask) { 495 * @param windowsAbsolutePath Windows absolute path of the 496 * key as a byte-encoded string. 497 * @param mask1 Preferred Windows security mask. 498 * @param mask2 Alternate Windows security mask. 499 * @return Windows registry key's handle. 500 * @see #openKey(int) 501 * @see #openKey(int, byte[],int) 502 * @see #closeKey(int) 503 */ 504 private int openKey(byte[] windowsAbsolutePath, int mask1, int mask2) { 505 /* Check if key's path is short enough be opened at once 506 otherwise use a path-splitting procedure */ 507 if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) { 508 int[] result = WindowsRegOpenKey1(rootNativeHandle(), 509 windowsAbsolutePath, mask1); 510 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1) 511 result = WindowsRegOpenKey1(rootNativeHandle(), 512 windowsAbsolutePath, mask2); 513 514 if (result[ERROR_CODE] != ERROR_SUCCESS) { 515 logger().warning("Could not open windows " 516 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 517 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 518 ". Windows RegOpenKey(...) returned error code " + 519 result[ERROR_CODE] + "."); 520 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE; 521 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) { 522 throw new SecurityException("Could not open windows " 523 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 524 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 525 ": Access denied"); 526 } 527 } 528 return result[NATIVE_HANDLE]; 529 } else { 530 return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2); 531 } 532 } 533 534 /** 535 * Opens Windows registry key at a given relative path 536 * with respect to a given Windows registry key. 537 * @param windowsAbsolutePath Windows relative path of the 538 * key as a byte-encoded string. 539 * @param nativeHandle handle to the base Windows key. 540 * @param mask1 Preferred Windows security mask. 541 * @param mask2 Alternate Windows security mask. 542 * @return Windows registry key's handle. 543 * @see #openKey(int) 544 * @see #openKey(byte[],int) 545 * @see #closeKey(int) 546 */ 547 private int openKey(int nativeHandle, byte[] windowsRelativePath, 548 int mask1, int mask2) { 549 /* If the path is short enough open at once. Otherwise split the path */ 550 if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) { 551 int[] result = WindowsRegOpenKey1(nativeHandle, 552 windowsRelativePath, mask1); 553 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1) 554 result = WindowsRegOpenKey1(nativeHandle, 555 windowsRelativePath, mask2); 556 557 if (result[ERROR_CODE] != ERROR_SUCCESS) { 558 logger().warning("Could not open windows " 559 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 560 " at root 0x" + Integer.toHexString(nativeHandle) + 561 ". Windows RegOpenKey(...) returned error code " + 562 result[ERROR_CODE] + "."); 563 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE; 564 } 565 return result[NATIVE_HANDLE]; 566 } else { 567 int separatorPosition = -1; 568 // Be greedy - open the longest possible path 569 for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) { 570 if (windowsRelativePath[i] == ((byte)'\\')) { 571 separatorPosition = i; 572 break; 573 } 574 } 575 // Split the path and do the recursion 576 byte[] nextRelativeRoot = new byte[separatorPosition+1]; 577 System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0, 578 separatorPosition); 579 nextRelativeRoot[separatorPosition] = 0; 587 return NULL_NATIVE_HANDLE; 588 } 589 int result = openKey(nextNativeHandle, nextRelativePath, 590 mask1,mask2); 591 closeKey(nextNativeHandle); 592 return result; 593 } 594 } 595 596 /** 597 * Closes Windows registry key. 598 * Logs a warning if Windows registry is unavailable. 599 * @param key's Windows registry handle. 600 * @see #openKey(int) 601 * @see #openKey(byte[],int) 602 * @see #openKey(int, byte[],int) 603 */ 604 private void closeKey(int nativeHandle) { 605 int result = WindowsRegCloseKey(nativeHandle); 606 if (result != ERROR_SUCCESS) { 607 logger().warning("Could not close windows " 608 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 609 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 610 ". Windows RegCloseKey(...) returned error code " + result + "."); 611 } 612 } 613 614 /** 615 * Implements <tt>AbstractPreferences</tt> <tt>putSpi()</tt> method. 616 * Puts name-value pair into the underlying Windows registry node. 617 * Logs a warning, if Windows registry is unavailable. 618 * @see #getSpi(String) 619 */ 620 protected void putSpi(String javaName, String value) { 621 int nativeHandle = openKey(KEY_SET_VALUE); 622 if (nativeHandle == NULL_NATIVE_HANDLE) { 623 isBackingStoreAvailable = false; 624 return; 625 } 626 int result = WindowsRegSetValueEx1(nativeHandle, 627 toWindowsName(javaName), toWindowsValueString(value)); 628 if (result != ERROR_SUCCESS) { 629 logger().warning("Could not assign value to key " + 630 byteArrayToString(toWindowsName(javaName))+ " at Windows registry node " 631 + byteArrayToString(windowsAbsolutePath()) + " at root 0x" 632 + Integer.toHexString(rootNativeHandle()) + 633 ". Windows RegSetValueEx(...) returned error code " + result + "."); 634 isBackingStoreAvailable = false; 635 } 636 closeKey(nativeHandle); 637 } 638 639 /** 640 * Implements <tt>AbstractPreferences</tt> <tt>getSpi()</tt> method. 641 * Gets a string value from the underlying Windows registry node. 642 * Logs a warning, if Windows registry is unavailable. 643 * @see #putSpi(String, String) 644 */ 645 protected String getSpi(String javaName) { 646 int nativeHandle = openKey(KEY_QUERY_VALUE); 647 if (nativeHandle == NULL_NATIVE_HANDLE) { 648 return null; 649 } 650 Object resultObject = WindowsRegQueryValueEx(nativeHandle, 651 toWindowsName(javaName)); 652 if (resultObject == null) { 653 closeKey(nativeHandle); 655 } 656 closeKey(nativeHandle); 657 return toJavaValueString((byte[]) resultObject); 658 } 659 660 /** 661 * Implements <tt>AbstractPreferences</tt> <tt>removeSpi()</tt> method. 662 * Deletes a string name-value pair from the underlying Windows registry 663 * node, if this value still exists. 664 * Logs a warning, if Windows registry is unavailable or key has already 665 * been deleted. 666 */ 667 protected void removeSpi(String key) { 668 int nativeHandle = openKey(KEY_SET_VALUE); 669 if (nativeHandle == NULL_NATIVE_HANDLE) { 670 return; 671 } 672 int result = 673 WindowsRegDeleteValue(nativeHandle, toWindowsName(key)); 674 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { 675 logger().warning("Could not delete windows registry " 676 + "value " + byteArrayToString(windowsAbsolutePath())+ "\\" + 677 toWindowsName(key) + " at root 0x" + 678 Integer.toHexString(rootNativeHandle()) + 679 ". Windows RegDeleteValue(...) returned error code " + 680 result + "."); 681 isBackingStoreAvailable = false; 682 } 683 closeKey(nativeHandle); 684 } 685 686 /** 687 * Implements <tt>AbstractPreferences</tt> <tt>keysSpi()</tt> method. 688 * Gets value names from the underlying Windows registry node. 689 * Throws a BackingStoreException and logs a warning, if 690 * Windows registry is unavailable. 691 */ 692 protected String[] keysSpi() throws BackingStoreException{ 693 // Find out the number of values 694 int nativeHandle = openKey(KEY_QUERY_VALUE); 695 if (nativeHandle == NULL_NATIVE_HANDLE) { 696 throw new BackingStoreException("Could not open windows" 697 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 698 " at root 0x" + Integer.toHexString(rootNativeHandle()) + "."); 699 } 700 int[] result = WindowsRegQueryInfoKey1(nativeHandle); 701 if (result[ERROR_CODE] != ERROR_SUCCESS) { 702 String info = "Could not query windows" 703 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 704 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 705 ". Windows RegQueryInfoKeyEx(...) returned error code " + 706 result[ERROR_CODE] + "."; 707 logger().warning(info); 708 throw new BackingStoreException(info); 709 } 710 int maxValueNameLength = result[MAX_VALUE_NAME_LENGTH]; 711 int valuesNumber = result[VALUES_NUMBER]; 712 if (valuesNumber == 0) { 713 closeKey(nativeHandle); 714 return new String[0]; 715 } 716 // Get the values 717 String[] valueNames = new String[valuesNumber]; 718 for (int i = 0; i < valuesNumber; i++) { 719 byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i, 720 maxValueNameLength+1); 721 if (windowsName == null) { 722 String info = 723 "Could not enumerate value #" + i + " of windows node " + 724 byteArrayToString(windowsAbsolutePath()) + " at root 0x" + 725 Integer.toHexString(rootNativeHandle()) + "."; 726 logger().warning(info); 727 throw new BackingStoreException(info); 728 } 729 valueNames[i] = toJavaName(windowsName); 730 } 731 closeKey(nativeHandle); 732 return valueNames; 733 } 734 735 /** 736 * Implements <tt>AbstractPreferences</tt> <tt>childrenNamesSpi()</tt> method. 737 * Calls Windows registry to retrive children of this node. 738 * Throws a BackingStoreException and logs a warning message, 739 * if Windows registry is not available. 740 */ 741 protected String[] childrenNamesSpi() throws BackingStoreException { 742 // Open key 743 int nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS| KEY_QUERY_VALUE); 744 if (nativeHandle == NULL_NATIVE_HANDLE) { 745 throw new BackingStoreException("Could not open windows" 746 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 747 " at root 0x" + Integer.toHexString(rootNativeHandle()) + "."); 748 } 749 // Get number of children 750 int[] result = WindowsRegQueryInfoKey1(nativeHandle); 751 if (result[ERROR_CODE] != ERROR_SUCCESS) { 752 String info = "Could not query windows" 753 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 754 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 755 ". Windows RegQueryInfoKeyEx(...) returned error code " + 756 result[ERROR_CODE] + "."; 757 logger().warning(info); 758 throw new BackingStoreException(info); 759 } 760 int maxKeyLength = result[MAX_KEY_LENGTH]; 761 int subKeysNumber = result[SUBKEYS_NUMBER]; 762 if (subKeysNumber == 0) { 763 closeKey(nativeHandle); 764 return new String[0]; 765 } 766 String[] subkeys = new String[subKeysNumber]; 767 String[] children = new String[subKeysNumber]; 768 // Get children 769 for (int i = 0; i < subKeysNumber; i++) { 770 byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i, 771 maxKeyLength+1); 772 if (windowsName == null) { 773 String info = 785 } 786 787 /** 788 * Implements <tt>Preferences</tt> <tt>flush()</tt> method. 789 * Flushes Windows registry changes to disk. 790 * Throws a BackingStoreException and logs a warning message if Windows 791 * registry is not available. 792 */ 793 public void flush() throws BackingStoreException{ 794 795 if (isRemoved()) { 796 parent.flush(); 797 return; 798 } 799 if (!isBackingStoreAvailable) { 800 throw new BackingStoreException( 801 "flush(): Backing store not available."); 802 } 803 int nativeHandle = openKey(KEY_READ); 804 if (nativeHandle == NULL_NATIVE_HANDLE) { 805 throw new BackingStoreException("Could not open windows" 806 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 807 " at root 0x" + Integer.toHexString(rootNativeHandle()) + "."); 808 } 809 int result = WindowsRegFlushKey1(nativeHandle); 810 if (result != ERROR_SUCCESS) { 811 String info = "Could not flush windows " 812 + "registry node " + byteArrayToString(windowsAbsolutePath()) 813 + " at root 0x" + Integer.toHexString(rootNativeHandle()) + 814 ". Windows RegFlushKey(...) returned error code " + result + "."; 815 logger().warning(info); 816 throw new BackingStoreException(info); 817 } 818 closeKey(nativeHandle); 819 } 820 821 822 /** 823 * Implements <tt>Preferences</tt> <tt>sync()</tt> method. 824 * Flushes Windows registry changes to disk. Equivalent to flush(). 825 * @see flush() 826 */ 827 public void sync() throws BackingStoreException{ 828 if (isRemoved()) 829 throw new IllegalStateException("Node has been removed"); 830 flush(); 831 } 832 833 /** 834 * Implements <tt>AbstractPreferences</tt> <tt>childSpi()</tt> method. 835 * Constructs a child node with a 836 * given name and creates its underlying Windows registry node, 837 * if it does not exist. 838 * Logs a warning message, if Windows Registry is unavailable. 839 */ 840 protected AbstractPreferences childSpi(String name) { 841 return new WindowsPreferences(this, name); 842 } 843 844 /** 845 * Implements <tt>AbstractPreferences</tt> <tt>removeNodeSpi()</tt> method. 846 * Deletes underlying Windows registry node. 847 * Throws a BackingStoreException and logs a warning, if Windows registry 848 * is not available. 849 */ 850 public void removeNodeSpi() throws BackingStoreException { 851 int parentNativeHandle = 852 ((WindowsPreferences)parent()).openKey(DELETE); 853 if (parentNativeHandle == NULL_NATIVE_HANDLE) { 854 throw new BackingStoreException("Could not open parent windows" 855 + "registry node of " + byteArrayToString(windowsAbsolutePath()) + 856 " at root 0x" + Integer.toHexString(rootNativeHandle()) + "."); 857 } 858 int result = 859 WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name())); 860 if (result != ERROR_SUCCESS) { 861 String info = "Could not delete windows " 862 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 863 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 864 ". Windows RegDeleteKeyEx(...) returned error code " + 865 result + "."; 866 logger().warning(info); 867 throw new BackingStoreException(info); 868 } 869 closeKey(parentNativeHandle); 870 } 871 872 /** 873 * Converts value's or node's name from its byte array representation to 874 * java string. Two encodings, simple and altBase64 are used. See 875 * {@link #toWindowsName(String) toWindowsName()} for a detailed 876 * description of encoding conventions. 877 * @param windowsNameArray Null-terminated byte array. 878 */ 879 private static String toJavaName(byte[] windowsNameArray) { 880 String windowsName = byteArrayToString(windowsNameArray); 881 // check if Alt64 882 if ((windowsName.length()>1) && 883 (windowsName.substring(0,2).equals("/!"))) { 884 return toJavaAlt64Name(windowsName); 885 } 886 StringBuffer javaName = new StringBuffer(); 887 char ch; 888 // Decode from simple encoding 889 for (int i = 0; i < windowsName.length(); i++){ 890 if ((ch = windowsName.charAt(i)) == '/') { 891 char next = ' '; 892 if ((windowsName.length() > i + 1) && 893 ((next = windowsName.charAt(i+1)) >= 'A') && (next <= 'Z')) { 894 ch = next; 895 i++; 896 } else if ((windowsName.length() > i + 1) && (next == '/')) { 897 ch = '\\'; 898 i++; 899 } 900 } else if (ch == '\\') { 901 ch = '/'; 902 } 903 javaName.append(ch); 904 } 905 return javaName.toString(); 906 } 907 908 /** 909 * Converts value's or node's name from its Windows representation to java 910 * string, using altBase64 encoding. See 911 * {@link #toWindowsName(String) toWindowsName()} for a detailed 912 * description of encoding conventions. 913 */ 914 915 private static String toJavaAlt64Name(String windowsName) { 916 byte[] byteBuffer = 917 Base64.altBase64ToByteArray(windowsName.substring(2)); 918 StringBuffer result = new StringBuffer(); 919 for (int i = 0; i < byteBuffer.length; i++) { 920 int firstbyte = (byteBuffer[i++] & 0xff); 921 int secondbyte = (byteBuffer[i] & 0xff); 922 result.append((char)((firstbyte << 8) + secondbyte)); 923 } 924 return result.toString(); 925 } 926 927 /** 928 * Converts value's or node's name to its Windows representation 929 * as a byte-encoded string. 930 * Two encodings, simple and altBase64 are used. 931 * <p> 932 * <i>Simple</i> encoding is used, if java string does not contain 933 * any characters less, than 0x0020, or greater, than 0x007f. 934 * Simple encoding adds "/" character to capital letters, i.e. 935 * "A" is encoded as "/A". Character '\' is encoded as '//', 936 * '/' is encoded as '\'. 937 * The constructed string is converted to byte array by truncating the 938 * highest byte and adding the terminating <tt>null</tt> character. 939 * <p> 940 * <i>altBase64</i> encoding is used, if java string does contain at least 941 * one character less, than 0x0020, or greater, than 0x007f. 942 * This encoding is marked by setting first two bytes of the 943 * Windows string to '/!'. The java name is then encoded using 944 * byteArrayToAltBase64() method from 945 * Base64 class. 946 */ 947 private static byte[] toWindowsName(String javaName) { 948 StringBuffer windowsName = new StringBuffer(); 949 for (int i = 0; i < javaName.length(); i++) { 950 char ch =javaName.charAt(i); 951 if ((ch < 0x0020)||(ch > 0x007f)) { 952 // If a non-trivial character encountered, use altBase64 953 return toWindowsAlt64Name(javaName); 954 } 955 if (ch == '\\') { 956 windowsName.append("//"); 957 } else if (ch == '/') { 958 windowsName.append('\\'); 959 } else if ((ch >= 'A') && (ch <='Z')) { 960 windowsName.append("/" + ch); 961 } else { 962 windowsName.append(ch); 963 } 964 } 965 return stringToByteArray(windowsName.toString()); 966 } 967 968 /** 969 * Converts value's or node's name to its Windows representation 970 * as a byte-encoded string, using altBase64 encoding. See 971 * {@link #toWindowsName(String) toWindowsName()} for a detailed 972 * description of encoding conventions. 973 */ 974 private static byte[] toWindowsAlt64Name(String javaName) { 975 byte[] javaNameArray = new byte[2*javaName.length()]; 976 // Convert to byte pairs 977 int counter = 0; 978 for (int i = 0; i < javaName.length();i++) { 979 int ch = javaName.charAt(i); 980 javaNameArray[counter++] = (byte)(ch >>> 8); 981 javaNameArray[counter++] = (byte)ch; 982 } 983 984 return stringToByteArray( 985 "/!" + Base64.byteArrayToAltBase64(javaNameArray)); 986 } 987 988 /** 989 * Converts value string from its Windows representation 990 * to java string. See 991 * {@link #toWindowsValueString(String) toWindowsValueString()} for the 992 * description of the encoding algorithm. 993 */ 994 private static String toJavaValueString(byte[] windowsNameArray) { 995 // Use modified native2ascii algorithm 996 String windowsName = byteArrayToString(windowsNameArray); 997 StringBuffer javaName = new StringBuffer(); 998 char ch; 999 for (int i = 0; i < windowsName.length(); i++){ 1000 if ((ch = windowsName.charAt(i)) == '/') { 1001 char next = ' '; 1002 1003 if (windowsName.length() > i + 1 && 1004 (next = windowsName.charAt(i + 1)) == 'u') { 1005 if (windowsName.length() < i + 6){ 1006 break; 1007 } else { 1008 ch = (char)Integer.parseInt 1009 (windowsName.substring(i + 2, i + 6), 16); 1010 i += 5; 1011 } 1012 } else 1013 if ((windowsName.length() > i + 1) && 1014 ((windowsName.charAt(i+1)) >= 'A') && (next <= 'Z')) { 1015 ch = next; 1016 i++; 1017 } else if ((windowsName.length() > i + 1) && 1018 (next == '/')) { 1019 ch = '\\'; 1020 i++; 1021 } 1022 } else if (ch == '\\') { 1023 ch = '/'; 1024 } 1025 javaName.append(ch); 1026 } 1027 return javaName.toString(); 1028 } 1029 1030 /** 1031 * Converts value string to it Windows representation. 1032 * as a byte-encoded string. 1033 * Encoding algorithm adds "/" character to capital letters, i.e. 1034 * "A" is encoded as "/A". Character '\' is encoded as '//', 1035 * '/' is encoded as '\'. 1036 * Then encoding scheme similar to jdk's native2ascii converter is used 1037 * to convert java string to a byte array of ASCII characters. 1038 */ 1039 private static byte[] toWindowsValueString(String javaName) { 1040 StringBuffer windowsName = new StringBuffer(); 1041 for (int i = 0; i < javaName.length(); i++) { 1042 char ch =javaName.charAt(i); 1043 if ((ch < 0x0020)||(ch > 0x007f)){ 1044 // write \udddd 1045 windowsName.append("/u"); 1046 String hex = Integer.toHexString(javaName.charAt(i)); 1047 StringBuffer hex4 = new StringBuffer(hex); 1048 hex4.reverse(); 1049 int len = 4 - hex4.length(); 1050 for (int j = 0; j < len; j++){ 1051 hex4.append('0'); 1052 } 1053 for (int j = 0; j < 4; j++){ 1054 windowsName.append(hex4.charAt(3 - j)); 1055 } 1056 } else if (ch == '\\') { 1057 windowsName.append("//"); 1058 } else if (ch == '/') { 1059 windowsName.append('\\'); 1060 } else if ((ch >= 'A') && (ch <='Z')) { 1061 windowsName.append("/" + ch); 1062 } else { 1063 windowsName.append(ch); 1064 } 1065 } 1066 return stringToByteArray(windowsName.toString()); 1067 } 1068 1069 /** 1070 * Returns native handle for the top Windows node for this node. 1071 */ 1072 private int rootNativeHandle() { 1073 return (isUserNode()? USER_ROOT_NATIVE_HANDLE : 1074 SYSTEM_ROOT_NATIVE_HANDLE); 1075 } 1076 1077 /** 1078 * Returns this java string as a null-terminated byte array 1079 */ 1080 private static byte[] stringToByteArray(String str) { 1081 byte[] result = new byte[str.length()+1]; 1082 for (int i = 0; i < str.length(); i++) { 1083 result[i] = (byte) str.charAt(i); 1084 } 1085 result[str.length()] = 0; 1086 return result; 1087 } 1088 1089 /** 1090 * Converts a null-terminated byte array to java string 1091 */ 1092 private static String byteArrayToString(byte[] array) { 1093 StringBuffer result = new StringBuffer(); 1094 for (int i = 0; i < array.length - 1; i++) { 1095 result.append((char)array[i]); 1096 } 1097 return result.toString(); 1098 } 1099 1100 /** 1101 * Empty, never used implementation of AbstractPreferences.flushSpi(). 1102 */ 1103 protected void flushSpi() throws BackingStoreException { 1104 // assert false; 1105 } 1106 1107 /** 1108 * Empty, never used implementation of AbstractPreferences.flushSpi(). 1109 */ 1110 protected void syncSpi() throws BackingStoreException { 1111 // assert false; 1112 } 1113 | 45 */ 46 47 class WindowsPreferences extends AbstractPreferences { 48 49 static { 50 PrivilegedAction<Void> load = () -> { 51 System.loadLibrary("prefs"); 52 return null; 53 }; 54 AccessController.doPrivileged(load); 55 } 56 57 /** 58 * Logger for error messages 59 */ 60 private static PlatformLogger logger; 61 62 /** 63 * Windows registry path to <tt>Preferences</tt>'s root nodes. 64 */ 65 private static final byte[] WINDOWS_ROOT_PATH = 66 stringToByteArray("Software\\JavaSoft\\Prefs"); 67 68 /** 69 * Windows handles to <tt>HKEY_CURRENT_USER</tt> and 70 * <tt>HKEY_LOCAL_MACHINE</tt> hives. 71 */ 72 private static final int HKEY_CURRENT_USER = 0x80000001; 73 private static final int HKEY_LOCAL_MACHINE = 0x80000002; 74 75 /** 76 * Mount point for <tt>Preferences</tt>' user root. 77 */ 78 private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER; 79 80 /** 81 * Mount point for <tt>Preferences</tt>' system root. 82 */ 83 private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE; 84 85 /** 86 * Maximum byte-encoded path length for Windows native functions, 387 return result; 388 } 389 390 /** 391 * Constructs a <tt>WindowsPreferences</tt> node, creating underlying 392 * Windows registry node and all its Windows parents, if they are not yet 393 * created. 394 * Logs a warning message, if Windows Registry is unavailable. 395 */ 396 private WindowsPreferences(WindowsPreferences parent, String name) { 397 super(parent, name); 398 int parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ); 399 if (parentNativeHandle == NULL_NATIVE_HANDLE) { 400 // if here, openKey failed and logged 401 isBackingStoreAvailable = false; 402 return; 403 } 404 int[] result = 405 WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name)); 406 if (result[ERROR_CODE] != ERROR_SUCCESS) { 407 logger().warning("Could not create windows registry node " + 408 byteArrayToString(windowsAbsolutePath()) + 409 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 410 ". Windows RegCreateKeyEx(...) returned error code " + 411 result[ERROR_CODE] + "."); 412 isBackingStoreAvailable = false; 413 return; 414 } 415 newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY); 416 closeKey(parentNativeHandle); 417 closeKey(result[NATIVE_HANDLE]); 418 } 419 420 /** 421 * Constructs a root node creating the underlying 422 * Windows registry node and all of its parents, if they have not yet been 423 * created. 424 * Logs a warning message, if Windows Registry is unavailable. 425 * @param rootNativeHandle Native handle to one of Windows top level keys. 426 * @param rootDirectory Path to root directory, as a byte-encoded string. 427 */ 428 private WindowsPreferences(int rootNativeHandle, byte[] rootDirectory) { 429 super(null, ""); 430 int[] result = 431 WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory); 432 if (result[ERROR_CODE] != ERROR_SUCCESS) { 433 logger().warning("Could not open/create prefs root node " + 434 byteArrayToString(windowsAbsolutePath()) + 435 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 436 ". Windows RegCreateKeyEx(...) returned error code " + 437 result[ERROR_CODE] + "."); 438 isBackingStoreAvailable = false; 439 return; 440 } 441 // Check if a new node 442 newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY); 443 closeKey(result[NATIVE_HANDLE]); 444 } 445 446 /** 447 * Returns Windows absolute path of the current node as a byte array. 448 * Java "/" separator is transformed into Windows "\". 449 * @see Preferences#absolutePath() 450 */ 451 private byte[] windowsAbsolutePath() { 452 ByteArrayOutputStream bstream = new ByteArrayOutputStream(); 453 bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1); 454 StringTokenizer tokenizer = new StringTokenizer(absolutePath(), "/"); 455 while (tokenizer.hasMoreTokens()) { 456 bstream.write((byte)'\\'); 457 String nextName = tokenizer.nextToken(); 458 byte[] windowsNextName = toWindowsName(nextName); 459 bstream.write(windowsNextName, 0, windowsNextName.length-1); 460 } 461 bstream.write(0); 462 return bstream.toByteArray(); 463 } 464 465 /** 466 * Opens current node's underlying Windows registry key using a 467 * given security mask. 468 * @param securityMask Windows security mask. 469 * @return Windows registry key's handle. 470 * @see #openKey(byte[], int) 471 * @see #openKey(int, byte[], int) 472 * @see #closeKey(int) 473 */ 474 private int openKey(int securityMask) { 495 * @param windowsAbsolutePath Windows absolute path of the 496 * key as a byte-encoded string. 497 * @param mask1 Preferred Windows security mask. 498 * @param mask2 Alternate Windows security mask. 499 * @return Windows registry key's handle. 500 * @see #openKey(int) 501 * @see #openKey(int, byte[],int) 502 * @see #closeKey(int) 503 */ 504 private int openKey(byte[] windowsAbsolutePath, int mask1, int mask2) { 505 /* Check if key's path is short enough be opened at once 506 otherwise use a path-splitting procedure */ 507 if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) { 508 int[] result = WindowsRegOpenKey1(rootNativeHandle(), 509 windowsAbsolutePath, mask1); 510 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1) 511 result = WindowsRegOpenKey1(rootNativeHandle(), 512 windowsAbsolutePath, mask2); 513 514 if (result[ERROR_CODE] != ERROR_SUCCESS) { 515 logger().warning("Could not open windows registry node " + 516 byteArrayToString(windowsAbsolutePath()) + 517 " at root 0x" + 518 Integer.toHexString(rootNativeHandle()) + 519 ". Windows RegOpenKey(...) returned error code " + 520 result[ERROR_CODE] + "."); 521 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE; 522 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) { 523 throw new SecurityException( 524 "Could not open windows registry node " + 525 byteArrayToString(windowsAbsolutePath()) + 526 " at root 0x" + 527 Integer.toHexString(rootNativeHandle()) + 528 ": Access denied"); 529 } 530 } 531 return result[NATIVE_HANDLE]; 532 } else { 533 return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2); 534 } 535 } 536 537 /** 538 * Opens Windows registry key at a given relative path 539 * with respect to a given Windows registry key. 540 * @param windowsAbsolutePath Windows relative path of the 541 * key as a byte-encoded string. 542 * @param nativeHandle handle to the base Windows key. 543 * @param mask1 Preferred Windows security mask. 544 * @param mask2 Alternate Windows security mask. 545 * @return Windows registry key's handle. 546 * @see #openKey(int) 547 * @see #openKey(byte[],int) 548 * @see #closeKey(int) 549 */ 550 private int openKey(int nativeHandle, byte[] windowsRelativePath, 551 int mask1, int mask2) { 552 /* If the path is short enough open at once. Otherwise split the path */ 553 if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) { 554 int[] result = WindowsRegOpenKey1(nativeHandle, 555 windowsRelativePath, mask1); 556 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1) 557 result = WindowsRegOpenKey1(nativeHandle, 558 windowsRelativePath, mask2); 559 560 if (result[ERROR_CODE] != ERROR_SUCCESS) { 561 logger().warning("Could not open windows registry node " + 562 byteArrayToString(windowsAbsolutePath()) + 563 " at root 0x" + Integer.toHexString(nativeHandle) + 564 ". Windows RegOpenKey(...) returned error code " + 565 result[ERROR_CODE] + "."); 566 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE; 567 } 568 return result[NATIVE_HANDLE]; 569 } else { 570 int separatorPosition = -1; 571 // Be greedy - open the longest possible path 572 for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) { 573 if (windowsRelativePath[i] == ((byte)'\\')) { 574 separatorPosition = i; 575 break; 576 } 577 } 578 // Split the path and do the recursion 579 byte[] nextRelativeRoot = new byte[separatorPosition+1]; 580 System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0, 581 separatorPosition); 582 nextRelativeRoot[separatorPosition] = 0; 590 return NULL_NATIVE_HANDLE; 591 } 592 int result = openKey(nextNativeHandle, nextRelativePath, 593 mask1,mask2); 594 closeKey(nextNativeHandle); 595 return result; 596 } 597 } 598 599 /** 600 * Closes Windows registry key. 601 * Logs a warning if Windows registry is unavailable. 602 * @param key's Windows registry handle. 603 * @see #openKey(int) 604 * @see #openKey(byte[],int) 605 * @see #openKey(int, byte[],int) 606 */ 607 private void closeKey(int nativeHandle) { 608 int result = WindowsRegCloseKey(nativeHandle); 609 if (result != ERROR_SUCCESS) { 610 logger().warning("Could not close windows registry node " + 611 byteArrayToString(windowsAbsolutePath()) + 612 " at root 0x" + 613 Integer.toHexString(rootNativeHandle()) + 614 ". Windows RegCloseKey(...) returned error code " + 615 result + "."); 616 } 617 } 618 619 /** 620 * Implements <tt>AbstractPreferences</tt> <tt>putSpi()</tt> method. 621 * Puts name-value pair into the underlying Windows registry node. 622 * Logs a warning, if Windows registry is unavailable. 623 * @see #getSpi(String) 624 */ 625 protected void putSpi(String javaName, String value) { 626 int nativeHandle = openKey(KEY_SET_VALUE); 627 if (nativeHandle == NULL_NATIVE_HANDLE) { 628 isBackingStoreAvailable = false; 629 return; 630 } 631 int result = WindowsRegSetValueEx1(nativeHandle, 632 toWindowsName(javaName), toWindowsValueString(value)); 633 if (result != ERROR_SUCCESS) { 634 logger().warning("Could not assign value to key " + 635 byteArrayToString(toWindowsName(javaName)) + 636 " at Windows registry node " + 637 byteArrayToString(windowsAbsolutePath()) + 638 " at root 0x" + 639 Integer.toHexString(rootNativeHandle()) + 640 ". Windows RegSetValueEx(...) returned error code " + 641 result + "."); 642 isBackingStoreAvailable = false; 643 } 644 closeKey(nativeHandle); 645 } 646 647 /** 648 * Implements <tt>AbstractPreferences</tt> <tt>getSpi()</tt> method. 649 * Gets a string value from the underlying Windows registry node. 650 * Logs a warning, if Windows registry is unavailable. 651 * @see #putSpi(String, String) 652 */ 653 protected String getSpi(String javaName) { 654 int nativeHandle = openKey(KEY_QUERY_VALUE); 655 if (nativeHandle == NULL_NATIVE_HANDLE) { 656 return null; 657 } 658 Object resultObject = WindowsRegQueryValueEx(nativeHandle, 659 toWindowsName(javaName)); 660 if (resultObject == null) { 661 closeKey(nativeHandle); 663 } 664 closeKey(nativeHandle); 665 return toJavaValueString((byte[]) resultObject); 666 } 667 668 /** 669 * Implements <tt>AbstractPreferences</tt> <tt>removeSpi()</tt> method. 670 * Deletes a string name-value pair from the underlying Windows registry 671 * node, if this value still exists. 672 * Logs a warning, if Windows registry is unavailable or key has already 673 * been deleted. 674 */ 675 protected void removeSpi(String key) { 676 int nativeHandle = openKey(KEY_SET_VALUE); 677 if (nativeHandle == NULL_NATIVE_HANDLE) { 678 return; 679 } 680 int result = 681 WindowsRegDeleteValue(nativeHandle, toWindowsName(key)); 682 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { 683 logger().warning("Could not delete windows registry value " + 684 byteArrayToString(windowsAbsolutePath()) + "\\" + 685 toWindowsName(key) + " at root 0x" + 686 Integer.toHexString(rootNativeHandle()) + 687 ". Windows RegDeleteValue(...) returned error code " + 688 result + "."); 689 isBackingStoreAvailable = false; 690 } 691 closeKey(nativeHandle); 692 } 693 694 /** 695 * Implements <tt>AbstractPreferences</tt> <tt>keysSpi()</tt> method. 696 * Gets value names from the underlying Windows registry node. 697 * Throws a BackingStoreException and logs a warning, if 698 * Windows registry is unavailable. 699 */ 700 protected String[] keysSpi() throws BackingStoreException{ 701 // Find out the number of values 702 int nativeHandle = openKey(KEY_QUERY_VALUE); 703 if (nativeHandle == NULL_NATIVE_HANDLE) { 704 throw new BackingStoreException( 705 "Could not open windows registry node " + 706 byteArrayToString(windowsAbsolutePath()) + 707 " at root 0x" + 708 Integer.toHexString(rootNativeHandle()) + "."); 709 } 710 int[] result = WindowsRegQueryInfoKey1(nativeHandle); 711 if (result[ERROR_CODE] != ERROR_SUCCESS) { 712 String info = "Could not query windows registry node " + 713 byteArrayToString(windowsAbsolutePath()) + 714 " at root 0x" + 715 Integer.toHexString(rootNativeHandle()) + 716 ". Windows RegQueryInfoKeyEx(...) returned error code " + 717 result[ERROR_CODE] + "."; 718 logger().warning(info); 719 throw new BackingStoreException(info); 720 } 721 int maxValueNameLength = result[MAX_VALUE_NAME_LENGTH]; 722 int valuesNumber = result[VALUES_NUMBER]; 723 if (valuesNumber == 0) { 724 closeKey(nativeHandle); 725 return new String[0]; 726 } 727 // Get the values 728 String[] valueNames = new String[valuesNumber]; 729 for (int i = 0; i < valuesNumber; i++) { 730 byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i, 731 maxValueNameLength+1); 732 if (windowsName == null) { 733 String info = 734 "Could not enumerate value #" + i + " of windows node " + 735 byteArrayToString(windowsAbsolutePath()) + " at root 0x" + 736 Integer.toHexString(rootNativeHandle()) + "."; 737 logger().warning(info); 738 throw new BackingStoreException(info); 739 } 740 valueNames[i] = toJavaName(windowsName); 741 } 742 closeKey(nativeHandle); 743 return valueNames; 744 } 745 746 /** 747 * Implements <tt>AbstractPreferences</tt> <tt>childrenNamesSpi()</tt> method. 748 * Calls Windows registry to retrive children of this node. 749 * Throws a BackingStoreException and logs a warning message, 750 * if Windows registry is not available. 751 */ 752 protected String[] childrenNamesSpi() throws BackingStoreException { 753 // Open key 754 int nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE); 755 if (nativeHandle == NULL_NATIVE_HANDLE) { 756 throw new BackingStoreException( 757 "Could not open windows registry node " + 758 byteArrayToString(windowsAbsolutePath()) + 759 " at root 0x" + 760 Integer.toHexString(rootNativeHandle()) + "."); 761 } 762 // Get number of children 763 int[] result = WindowsRegQueryInfoKey1(nativeHandle); 764 if (result[ERROR_CODE] != ERROR_SUCCESS) { 765 String info = "Could not query windows registry node " + 766 byteArrayToString(windowsAbsolutePath()) + 767 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 768 ". Windows RegQueryInfoKeyEx(...) returned error code " + 769 result[ERROR_CODE] + "."; 770 logger().warning(info); 771 throw new BackingStoreException(info); 772 } 773 int maxKeyLength = result[MAX_KEY_LENGTH]; 774 int subKeysNumber = result[SUBKEYS_NUMBER]; 775 if (subKeysNumber == 0) { 776 closeKey(nativeHandle); 777 return new String[0]; 778 } 779 String[] subkeys = new String[subKeysNumber]; 780 String[] children = new String[subKeysNumber]; 781 // Get children 782 for (int i = 0; i < subKeysNumber; i++) { 783 byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i, 784 maxKeyLength+1); 785 if (windowsName == null) { 786 String info = 798 } 799 800 /** 801 * Implements <tt>Preferences</tt> <tt>flush()</tt> method. 802 * Flushes Windows registry changes to disk. 803 * Throws a BackingStoreException and logs a warning message if Windows 804 * registry is not available. 805 */ 806 public void flush() throws BackingStoreException{ 807 808 if (isRemoved()) { 809 parent.flush(); 810 return; 811 } 812 if (!isBackingStoreAvailable) { 813 throw new BackingStoreException( 814 "flush(): Backing store not available."); 815 } 816 int nativeHandle = openKey(KEY_READ); 817 if (nativeHandle == NULL_NATIVE_HANDLE) { 818 throw new BackingStoreException( 819 "Could not open windows registry node " + 820 byteArrayToString(windowsAbsolutePath()) + 821 " at root 0x" + 822 Integer.toHexString(rootNativeHandle()) + "."); 823 } 824 int result = WindowsRegFlushKey1(nativeHandle); 825 if (result != ERROR_SUCCESS) { 826 String info = "Could not flush windows registry node " + 827 byteArrayToString(windowsAbsolutePath()) + 828 " at root 0x" + 829 Integer.toHexString(rootNativeHandle()) + 830 ". Windows RegFlushKey(...) returned error code " + 831 result + "."; 832 logger().warning(info); 833 throw new BackingStoreException(info); 834 } 835 closeKey(nativeHandle); 836 } 837 838 839 /** 840 * Implements <tt>Preferences</tt> <tt>sync()</tt> method. 841 * Flushes Windows registry changes to disk. Equivalent to flush(). 842 * @see flush() 843 */ 844 public void sync() throws BackingStoreException{ 845 if (isRemoved()) 846 throw new IllegalStateException("Node has been removed"); 847 flush(); 848 } 849 850 /** 851 * Implements <tt>AbstractPreferences</tt> <tt>childSpi()</tt> method. 852 * Constructs a child node with a 853 * given name and creates its underlying Windows registry node, 854 * if it does not exist. 855 * Logs a warning message, if Windows Registry is unavailable. 856 */ 857 protected AbstractPreferences childSpi(String name) { 858 return new WindowsPreferences(this, name); 859 } 860 861 /** 862 * Implements <tt>AbstractPreferences</tt> <tt>removeNodeSpi()</tt> method. 863 * Deletes underlying Windows registry node. 864 * Throws a BackingStoreException and logs a warning, if Windows registry 865 * is not available. 866 */ 867 public void removeNodeSpi() throws BackingStoreException { 868 int parentNativeHandle = 869 ((WindowsPreferences)parent()).openKey(DELETE); 870 if (parentNativeHandle == NULL_NATIVE_HANDLE) { 871 throw new BackingStoreException( 872 "Could not open parent windows registry node of " + 873 byteArrayToString(windowsAbsolutePath()) + 874 " at root 0x" + 875 Integer.toHexString(rootNativeHandle()) + "."); 876 } 877 int result = 878 WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name())); 879 if (result != ERROR_SUCCESS) { 880 String info = "Could not delete windows registry node " + 881 byteArrayToString(windowsAbsolutePath()) + 882 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 883 ". Windows RegDeleteKeyEx(...) returned error code " + 884 result + "."; 885 logger().warning(info); 886 throw new BackingStoreException(info); 887 } 888 closeKey(parentNativeHandle); 889 } 890 891 /** 892 * Converts value's or node's name from its byte array representation to 893 * java string. Two encodings, simple and altBase64 are used. See 894 * {@link #toWindowsName(String) toWindowsName()} for a detailed 895 * description of encoding conventions. 896 * @param windowsNameArray Null-terminated byte array. 897 */ 898 private static String toJavaName(byte[] windowsNameArray) { 899 String windowsName = byteArrayToString(windowsNameArray); 900 // check if Alt64 901 if ((windowsName.length() > 1) && 902 (windowsName.substring(0, 2).equals("/!"))) { 903 return toJavaAlt64Name(windowsName); 904 } 905 StringBuilder javaName = new StringBuilder(); 906 char ch; 907 // Decode from simple encoding 908 for (int i = 0; i < windowsName.length(); i++) { 909 if ((ch = windowsName.charAt(i)) == '/') { 910 char next = ' '; 911 if ((windowsName.length() > i + 1) && 912 ((next = windowsName.charAt(i+1)) >= 'A') && 913 (next <= 'Z')) { 914 ch = next; 915 i++; 916 } else if ((windowsName.length() > i + 1) && 917 (next == '/')) { 918 ch = '\\'; 919 i++; 920 } 921 } else if (ch == '\\') { 922 ch = '/'; 923 } 924 javaName.append(ch); 925 } 926 return javaName.toString(); 927 } 928 929 /** 930 * Converts value's or node's name from its Windows representation to java 931 * string, using altBase64 encoding. See 932 * {@link #toWindowsName(String) toWindowsName()} for a detailed 933 * description of encoding conventions. 934 */ 935 936 private static String toJavaAlt64Name(String windowsName) { 937 byte[] byteBuffer = 938 Base64.altBase64ToByteArray(windowsName.substring(2)); 939 StringBuilder result = new StringBuilder(); 940 for (int i = 0; i < byteBuffer.length; i++) { 941 int firstbyte = (byteBuffer[i++] & 0xff); 942 int secondbyte = (byteBuffer[i] & 0xff); 943 result.append((char)((firstbyte << 8) + secondbyte)); 944 } 945 return result.toString(); 946 } 947 948 /** 949 * Converts value's or node's name to its Windows representation 950 * as a byte-encoded string. 951 * Two encodings, simple and altBase64 are used. 952 * <p> 953 * <i>Simple</i> encoding is used, if java string does not contain 954 * any characters less, than 0x0020, or greater, than 0x007f. 955 * Simple encoding adds "/" character to capital letters, i.e. 956 * "A" is encoded as "/A". Character '\' is encoded as '//', 957 * '/' is encoded as '\'. 958 * The constructed string is converted to byte array by truncating the 959 * highest byte and adding the terminating <tt>null</tt> character. 960 * <p> 961 * <i>altBase64</i> encoding is used, if java string does contain at least 962 * one character less, than 0x0020, or greater, than 0x007f. 963 * This encoding is marked by setting first two bytes of the 964 * Windows string to '/!'. The java name is then encoded using 965 * byteArrayToAltBase64() method from 966 * Base64 class. 967 */ 968 private static byte[] toWindowsName(String javaName) { 969 StringBuilder windowsName = new StringBuilder(); 970 for (int i = 0; i < javaName.length(); i++) { 971 char ch = javaName.charAt(i); 972 if ((ch < 0x0020) || (ch > 0x007f)) { 973 // If a non-trivial character encountered, use altBase64 974 return toWindowsAlt64Name(javaName); 975 } 976 if (ch == '\\') { 977 windowsName.append("//"); 978 } else if (ch == '/') { 979 windowsName.append('\\'); 980 } else if ((ch >= 'A') && (ch <='Z')) { 981 windowsName.append('/').append(ch); 982 } else { 983 windowsName.append(ch); 984 } 985 } 986 return stringToByteArray(windowsName.toString()); 987 } 988 989 /** 990 * Converts value's or node's name to its Windows representation 991 * as a byte-encoded string, using altBase64 encoding. See 992 * {@link #toWindowsName(String) toWindowsName()} for a detailed 993 * description of encoding conventions. 994 */ 995 private static byte[] toWindowsAlt64Name(String javaName) { 996 byte[] javaNameArray = new byte[2*javaName.length()]; 997 // Convert to byte pairs 998 int counter = 0; 999 for (int i = 0; i < javaName.length();i++) { 1000 int ch = javaName.charAt(i); 1001 javaNameArray[counter++] = (byte)(ch >>> 8); 1002 javaNameArray[counter++] = (byte)ch; 1003 } 1004 1005 return stringToByteArray("/!" + 1006 Base64.byteArrayToAltBase64(javaNameArray)); 1007 } 1008 1009 /** 1010 * Converts value string from its Windows representation 1011 * to java string. See 1012 * {@link #toWindowsValueString(String) toWindowsValueString()} for the 1013 * description of the encoding algorithm. 1014 */ 1015 private static String toJavaValueString(byte[] windowsNameArray) { 1016 // Use modified native2ascii algorithm 1017 String windowsName = byteArrayToString(windowsNameArray); 1018 StringBuilder javaName = new StringBuilder(); 1019 char ch; 1020 for (int i = 0; i < windowsName.length(); i++){ 1021 if ((ch = windowsName.charAt(i)) == '/') { 1022 char next = ' '; 1023 1024 if (windowsName.length() > i + 1 && 1025 (next = windowsName.charAt(i + 1)) == 'u') { 1026 if (windowsName.length() < i + 6) { 1027 break; 1028 } else { 1029 ch = (char)Integer.parseInt( 1030 windowsName.substring(i + 2, i + 6), 16); 1031 i += 5; 1032 } 1033 } else 1034 if ((windowsName.length() > i + 1) && 1035 ((windowsName.charAt(i+1)) >= 'A') && 1036 (next <= 'Z')) { 1037 ch = next; 1038 i++; 1039 } else if ((windowsName.length() > i + 1) && 1040 (next == '/')) { 1041 ch = '\\'; 1042 i++; 1043 } 1044 } else if (ch == '\\') { 1045 ch = '/'; 1046 } 1047 javaName.append(ch); 1048 } 1049 return javaName.toString(); 1050 } 1051 1052 /** 1053 * Converts value string to it Windows representation. 1054 * as a byte-encoded string. 1055 * Encoding algorithm adds "/" character to capital letters, i.e. 1056 * "A" is encoded as "/A". Character '\' is encoded as '//', 1057 * '/' is encoded as '\'. 1058 * Then encoding scheme similar to jdk's native2ascii converter is used 1059 * to convert java string to a byte array of ASCII characters. 1060 */ 1061 private static byte[] toWindowsValueString(String javaName) { 1062 StringBuilder windowsName = new StringBuilder(); 1063 for (int i = 0; i < javaName.length(); i++) { 1064 char ch = javaName.charAt(i); 1065 if ((ch < 0x0020) || (ch > 0x007f)){ 1066 // write \udddd 1067 windowsName.append("/u"); 1068 String hex = Integer.toHexString(javaName.charAt(i)); 1069 StringBuilder hex4 = new StringBuilder(hex); 1070 hex4.reverse(); 1071 int len = 4 - hex4.length(); 1072 for (int j = 0; j < len; j++){ 1073 hex4.append('0'); 1074 } 1075 for (int j = 0; j < 4; j++){ 1076 windowsName.append(hex4.charAt(3 - j)); 1077 } 1078 } else if (ch == '\\') { 1079 windowsName.append("//"); 1080 } else if (ch == '/') { 1081 windowsName.append('\\'); 1082 } else if ((ch >= 'A') && (ch <='Z')) { 1083 windowsName.append('/').append(ch); 1084 } else { 1085 windowsName.append(ch); 1086 } 1087 } 1088 return stringToByteArray(windowsName.toString()); 1089 } 1090 1091 /** 1092 * Returns native handle for the top Windows node for this node. 1093 */ 1094 private int rootNativeHandle() { 1095 return (isUserNode() 1096 ? USER_ROOT_NATIVE_HANDLE 1097 : SYSTEM_ROOT_NATIVE_HANDLE); 1098 } 1099 1100 /** 1101 * Returns this java string as a null-terminated byte array 1102 */ 1103 private static byte[] stringToByteArray(String str) { 1104 byte[] result = new byte[str.length()+1]; 1105 for (int i = 0; i < str.length(); i++) { 1106 result[i] = (byte) str.charAt(i); 1107 } 1108 result[str.length()] = 0; 1109 return result; 1110 } 1111 1112 /** 1113 * Converts a null-terminated byte array to java string 1114 */ 1115 private static String byteArrayToString(byte[] array) { 1116 StringBuilder result = new StringBuilder(); 1117 for (int i = 0; i < array.length - 1; i++) { 1118 result.append((char)array[i]); 1119 } 1120 return result.toString(); 1121 } 1122 1123 /** 1124 * Empty, never used implementation of AbstractPreferences.flushSpi(). 1125 */ 1126 protected void flushSpi() throws BackingStoreException { 1127 // assert false; 1128 } 1129 1130 /** 1131 * Empty, never used implementation of AbstractPreferences.flushSpi(). 1132 */ 1133 protected void syncSpi() throws BackingStoreException { 1134 // assert false; 1135 } 1136 |