4632N/A/*
4632N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
4632N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4632N/A *
4632N/A * This code is free software; you can redistribute it and/or modify it
4632N/A * under the terms of the GNU General Public License version 2 only, as
4632N/A * published by the Free Software Foundation. Oracle designates this
4632N/A * particular file as subject to the "Classpath" exception as provided
4632N/A * by Oracle in the LICENSE file that accompanied this code.
4632N/A *
4632N/A * This code is distributed in the hope that it will be useful, but WITHOUT
4632N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4632N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
4632N/A * version 2 for more details (a copy is included in the LICENSE file that
4632N/A * accompanied this code).
4632N/A *
4632N/A * You should have received a copy of the GNU General Public License version
4632N/A * 2 along with this work; if not, write to the Free Software Foundation,
4632N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
4632N/A *
4632N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
4632N/A * or visit www.oracle.com if you need additional information or have any
4632N/A * questions.
4632N/A */
4632N/A
4632N/A/*
4632N/A Hierarchical storage layout:
4632N/A
4632N/A <dict>
4632N/A <key>/</key>
4632N/A <dict>
4632N/A <key>foo</key>
4632N/A <string>/foo's value</string>
4632N/A <key>foo/</key>
4632N/A <dict>
4632N/A <key>bar</key>
4632N/A <string>/foo/bar's value</string>
4632N/A </dict>
4632N/A </dict>
4632N/A </dict>
4632N/A
4632N/A Java pref nodes are stored in several different files. Pref nodes
4632N/A with at least three components in the node name (e.g. /com/MyCompany/MyApp/)
4632N/A are stored in a CF prefs file with the first three components as the name.
4632N/A This way, all preferences for MyApp end up in com.MyCompany.MyApp.plist .
4632N/A Pref nodes with shorter names are stored in com.apple.java.util.prefs.plist
4632N/A
4632N/A The filesystem is assumed to be case-insensitive (like HFS+).
4632N/A Java pref node names are case-sensitive. If two pref node names differ
4632N/A only in case, they may end up in the same pref file. This is ok
4632N/A because the CF keys identifying the node span the entire absolute path
4632N/A to the node and are case-sensitive.
4632N/A
4632N/A Java node names may contain '.' . When mapping to the CF file name,
4632N/A these dots are left as-is, even though '/' is mapped to '.' .
4632N/A This is ok because the CF key contains the correct node name.
4632N/A*/
4632N/A
4632N/A
4632N/A
4632N/A#include <CoreFoundation/CoreFoundation.h>
4632N/A
4632N/A#include "jni_util.h"
4632N/A#include "jlong.h"
4632N/A#include "jvm.h"
4632N/A
4632N/A
4632N/A// Throw an OutOfMemoryError with the given message.
4632N/Astatic void throwOutOfMemoryError(JNIEnv *env, const char *msg)
4632N/A{
4632N/A static jclass exceptionClass = NULL;
4632N/A jclass c;
4632N/A
4632N/A if (exceptionClass) {
4632N/A c = exceptionClass;
4632N/A } else {
4632N/A c = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
4632N/A if ((*env)->ExceptionOccurred(env)) return;
4632N/A exceptionClass = (*env)->NewGlobalRef(env, c);
4632N/A }
4632N/A
4632N/A (*env)->ThrowNew(env, c, msg);
4632N/A}
4632N/A
4632N/A
4632N/A// throwIfNull macro
4632N/A// If var is NULL, throw an OutOfMemoryError and goto badvar.
4632N/A// var must be a variable. env must be the current JNIEnv.
4632N/A// fixme throw BackingStoreExceptions sometimes?
4632N/A#define throwIfNull(var, msg) \
4632N/A do { \
4632N/A if (var == NULL) { \
4632N/A throwOutOfMemoryError(env, msg); \
4632N/A goto bad##var; \
4632N/A } \
4632N/A } while (0)
4632N/A
4632N/A
4632N/A// Converts CFNumber, CFBoolean, CFString to CFString
4632N/A// returns NULL if value is of some other type
4632N/A// throws and returns NULL on memory error
4632N/A// result must be released (even if value was already a CFStringRef)
4632N/A// value must not be null
4632N/Astatic CFStringRef copyToCFString(JNIEnv *env, CFTypeRef value)
4632N/A{
4632N/A CFStringRef result;
4632N/A CFTypeID type;
4632N/A
4632N/A type = CFGetTypeID(value);
4632N/A
4632N/A if (type == CFStringGetTypeID()) {
4632N/A result = (CFStringRef)CFRetain(value);
4632N/A }
4632N/A else if (type == CFBooleanGetTypeID()) {
4632N/A // Java Preferences API expects "true" and "false" for boolean values.
4632N/A result = CFStringCreateCopy(NULL, (value == kCFBooleanTrue) ? CFSTR("true") : CFSTR("false"));
4632N/A throwIfNull(result, "copyToCFString failed");
4632N/A }
4632N/A else if (type == CFNumberGetTypeID()) {
4632N/A CFNumberRef number = (CFNumberRef) value;
4632N/A if (CFNumberIsFloatType(number)) {
4632N/A double d;
4632N/A CFNumberGetValue(number, kCFNumberDoubleType, &d);
4632N/A result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%g"), d);
4632N/A throwIfNull(result, "copyToCFString failed");
4632N/A }
4632N/A else {
4632N/A long l;
4632N/A CFNumberGetValue(number, kCFNumberLongType, &l);
4632N/A result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%ld"), l);
4632N/A throwIfNull(result, "copyToCFString failed");
4632N/A }
4632N/A }
4632N/A else {
4632N/A // unknown type - return NULL
4632N/A result = NULL;
4632N/A }
4632N/A
4632N/A badresult:
4632N/A return result;
4632N/A}
4632N/A
4632N/A
4632N/A// Create a Java string from the given CF string.
4632N/A// returns NULL if cfString is NULL
4632N/A// throws and returns NULL on memory error
4632N/Astatic jstring toJavaString(JNIEnv *env, CFStringRef cfString)
4632N/A{
4632N/A if (cfString == NULL) {
4632N/A return NULL;
4632N/A } else {
4632N/A jstring javaString = NULL;
4632N/A
4632N/A CFIndex length = CFStringGetLength(cfString);
4632N/A const UniChar *constchars = CFStringGetCharactersPtr(cfString);
4632N/A if (constchars) {
4632N/A javaString = (*env)->NewString(env, constchars, length);
4632N/A } else {
4632N/A UniChar *chars = malloc(length * sizeof(UniChar));
4632N/A throwIfNull(chars, "toJavaString failed");
4632N/A CFStringGetCharacters(cfString, CFRangeMake(0, length), chars);
4632N/A javaString = (*env)->NewString(env, chars, length);
4632N/A free(chars);
4632N/A }
4632N/A badchars:
4632N/A return javaString;
4632N/A }
4632N/A}
4632N/A
4632N/A
4632N/A
4632N/A// Create a CF string from the given Java string.
4632N/A// returns NULL if javaString is NULL
4632N/A// throws and returns NULL on memory error
4632N/Astatic CFStringRef toCF(JNIEnv *env, jstring javaString)
4632N/A{
4632N/A if (javaString == NULL) {
4632N/A return NULL;
4632N/A } else {
4632N/A CFStringRef result = NULL;
4632N/A jsize length = (*env)->GetStringLength(env, javaString);
4632N/A const jchar *chars = (*env)->GetStringChars(env, javaString, NULL);
4632N/A throwIfNull(chars, "toCF failed");
4632N/A result =
4632N/A CFStringCreateWithCharacters(NULL, (const UniChar *)chars, length);
4632N/A (*env)->ReleaseStringChars(env, javaString, chars);
4632N/A throwIfNull(result, "toCF failed");
4632N/A badchars:
4632N/A badresult:
4632N/A return result;
4632N/A }
4632N/A}
4632N/A
4632N/A
4632N/A// Create an empty Java string array of the given size.
4632N/A// Throws and returns NULL on error.
4632N/Astatic jarray createJavaStringArray(JNIEnv *env, CFIndex count)
4632N/A{
4632N/A static jclass stringClass = NULL;
4632N/A jclass c;
4632N/A
4632N/A if (stringClass) {
4632N/A c = stringClass;
4632N/A } else {
4632N/A c = (*env)->FindClass(env, "java/lang/String");
4632N/A if ((*env)->ExceptionOccurred(env)) return NULL;
4632N/A stringClass = (*env)->NewGlobalRef(env, c);
4632N/A }
4632N/A
4632N/A return (*env)->NewObjectArray(env, count, c, NULL); // AWT_THREADING Safe (known object)
4632N/A}
4632N/A
4632N/A
4632N/A// Java accessors for CF constants.
4632N/AJNIEXPORT jlong JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_currentUser(JNIEnv *env,
4632N/A jobject klass)
4632N/A{
4632N/A return ptr_to_jlong(kCFPreferencesCurrentUser);
4632N/A}
4632N/A
4632N/AJNIEXPORT jlong JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_anyUser(JNIEnv *env, jobject klass)
4632N/A{
4632N/A return ptr_to_jlong(kCFPreferencesAnyUser);
4632N/A}
4632N/A
4632N/AJNIEXPORT jlong JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_currentHost(JNIEnv *env,
4632N/A jobject klass)
4632N/A{
4632N/A return ptr_to_jlong(kCFPreferencesCurrentHost);
4632N/A}
4632N/A
4632N/AJNIEXPORT jlong JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_anyHost(JNIEnv *env, jobject klass)
4632N/A{
4632N/A return ptr_to_jlong(kCFPreferencesAnyHost);
4632N/A}
4632N/A
4632N/A
4632N/A// Create an empty node.
4632N/A// Does not store the node in any prefs file.
4632N/A// returns NULL on memory error
4632N/Astatic CFMutableDictionaryRef createEmptyNode(void)
4632N/A{
4632N/A return CFDictionaryCreateMutable(NULL, 0,
4632N/A &kCFTypeDictionaryKeyCallBacks,
4632N/A &kCFTypeDictionaryValueCallBacks);
4632N/A}
4632N/A
4632N/A
4632N/A// Create a string that consists of path minus its last component.
4632N/A// path must end with '/'
4632N/A// The result will end in '/' (unless path itself is '/')
4632N/Astatic CFStringRef copyParentOf(CFStringRef path)
4632N/A{
4632N/A CFRange searchRange;
4632N/A CFRange slashRange;
4632N/A CFRange parentRange;
4632N/A Boolean found;
4632N/A
4632N/A searchRange = CFRangeMake(0, CFStringGetLength(path) - 1);
4632N/A found = CFStringFindWithOptions(path, CFSTR("/"), searchRange,
4632N/A kCFCompareBackwards, &slashRange);
4632N/A if (!found) return CFSTR("");
4632N/A parentRange = CFRangeMake(0, slashRange.location + 1); // include '/'
4632N/A return CFStringCreateWithSubstring(NULL, path, parentRange);
4632N/A}
4632N/A
4632N/A
4632N/A// Create a string that consists of path's last component.
4632N/A// path must end with '/'
4632N/A// The result will end in '/'.
4632N/A// The result will not start with '/' (unless path itself is '/')
4632N/Astatic CFStringRef copyChildOf(CFStringRef path)
4632N/A{
4632N/A CFRange searchRange;
4632N/A CFRange slashRange;
4632N/A CFRange childRange;
4632N/A Boolean found;
4632N/A CFIndex length = CFStringGetLength(path);
4632N/A
4632N/A searchRange = CFRangeMake(0, length - 1);
4632N/A found = CFStringFindWithOptions(path, CFSTR("/"), searchRange,
4632N/A kCFCompareBackwards, &slashRange);
4632N/A if (!found) return CFSTR("");
4632N/A childRange = CFRangeMake(slashRange.location + 1,
4632N/A length - slashRange.location - 1); // skip '/'
4632N/A return CFStringCreateWithSubstring(NULL, path, childRange);
4632N/A}
4632N/A
4632N/A
4632N/A// Return the first three components of path, with leading and trailing '/'.
4632N/A// If path does not have three components, return NULL.
4632N/A// path must begin and end in '/'
4632N/Astatic CFStringRef copyFirstThreeComponentsOf(CFStringRef path)
4632N/A{
4632N/A CFRange searchRange;
4632N/A CFRange slashRange;
4632N/A CFRange prefixRange;
4632N/A CFStringRef prefix;
4632N/A Boolean found;
4632N/A CFIndex length = CFStringGetLength(path);
4632N/A
4632N/A searchRange = CFRangeMake(1, length - 1); // skip leading '/'
4632N/A found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,
4632N/A &slashRange);
4632N/A if (!found) return NULL; // no second slash!
4632N/A
4632N/A searchRange = CFRangeMake(slashRange.location + 1,
4632N/A length - slashRange.location - 1);
4632N/A found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,
4632N/A &slashRange);
4632N/A if (!found) return NULL; // no third slash!
4632N/A
4632N/A searchRange = CFRangeMake(slashRange.location + 1,
4632N/A length - slashRange.location - 1);
4632N/A found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,
4632N/A &slashRange);
4632N/A if (!found) return NULL; // no fourth slash!
4632N/A
4632N/A prefixRange = CFRangeMake(0, slashRange.location + 1); // keep last '/'
4632N/A prefix = CFStringCreateWithSubstring(NULL, path, prefixRange);
4632N/A
4632N/A return prefix;
4632N/A}
4632N/A
4632N/A
4632N/A// Copy the CFPreferences key and value at the base of path's tree.
4632N/A// path must end in '/'
4632N/A// topKey or topValue may be NULL
4632N/A// Returns NULL on error or if there is no tree for path in this file.
4632N/Astatic void copyTreeForPath(CFStringRef path, CFStringRef name,
4632N/A CFStringRef user, CFStringRef host,
4632N/A CFStringRef *topKey, CFDictionaryRef *topValue)
4632N/A{
4632N/A CFStringRef key;
4632N/A CFPropertyListRef value;
4632N/A
4632N/A if (topKey) *topKey = NULL;
4632N/A if (topValue) *topValue = NULL;
4632N/A
4632N/A if (CFEqual(name, CFSTR("com.apple.java.util.prefs"))) {
4632N/A // Top-level file. Only key "/" is an acceptable root.
4632N/A key = (CFStringRef) CFRetain(CFSTR("/"));
4632N/A } else {
4632N/A // Second-level file. Key must be the first three components of path.
4632N/A key = copyFirstThreeComponentsOf(path);
4632N/A if (!key) return;
4632N/A }
4632N/A
4632N/A value = CFPreferencesCopyValue(key, name, user, host);
4632N/A if (value) {
4632N/A if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
4632N/A // (key, value) is acceptable
4632N/A if (topKey) *topKey = (CFStringRef)CFRetain(key);
4632N/A if (topValue) *topValue = (CFDictionaryRef)CFRetain(value);
4632N/A }
4632N/A CFRelease(value);
4632N/A }
4632N/A CFRelease(key);
4632N/A}
4632N/A
4632N/A
4632N/A// Find the node for path in the given tree.
4632N/A// Returns NULL on error or if path doesn't have a node in this tree.
4632N/A// path must end in '/'
4632N/Astatic CFDictionaryRef copyNodeInTree(CFStringRef path, CFStringRef topKey,
4632N/A CFDictionaryRef topValue)
4632N/A{
4632N/A CFMutableStringRef p;
4632N/A CFDictionaryRef result = NULL;
4632N/A
4632N/A p = CFStringCreateMutableCopy(NULL, 0, path);
4632N/A if (!p) return NULL;
4632N/A CFStringDelete(p, CFRangeMake(0, CFStringGetLength(topKey)));
4632N/A result = topValue;
4632N/A
4632N/A while (CFStringGetLength(p) > 0) {
4632N/A CFDictionaryRef child;
4632N/A CFStringRef part = NULL;
4632N/A CFRange slashRange = CFStringFind(p, CFSTR("/"), 0);
4632N/A // guaranteed to succeed because path must end in '/'
4632N/A CFRange partRange = CFRangeMake(0, slashRange.location + 1);
4632N/A part = CFStringCreateWithSubstring(NULL, p, partRange);
4632N/A if (!part) { result = NULL; break; }
4632N/A CFStringDelete(p, partRange);
4632N/A
4632N/A child = CFDictionaryGetValue(result, part);
4632N/A CFRelease(part);
4632N/A if (child && CFGetTypeID(child) == CFDictionaryGetTypeID()) {
4632N/A // continue search
4632N/A result = child;
4632N/A } else {
4632N/A // didn't find target node
4632N/A result = NULL;
4632N/A break;
4632N/A }
4632N/A }
4632N/A
4632N/A CFRelease(p);
4632N/A if (result) return (CFDictionaryRef)CFRetain(result);
4632N/A else return NULL;
4632N/A}
4632N/A
4632N/A
4632N/A// Return a retained copy of the node at path from the given file.
4632N/A// path must end in '/'
4632N/A// returns NULL if node doesn't exist.
4632N/A// returns NULL if the value for key "path" isn't a valid node.
4632N/Astatic CFDictionaryRef copyNodeIfPresent(CFStringRef path, CFStringRef name,
4632N/A CFStringRef user, CFStringRef host)
4632N/A{
4632N/A CFStringRef topKey;
4632N/A CFDictionaryRef topValue;
4632N/A CFDictionaryRef result;
4632N/A
4632N/A copyTreeForPath(path, name, user, host, &topKey, &topValue);
4632N/A if (!topKey) return NULL;
4632N/A
4632N/A result = copyNodeInTree(path, topKey, topValue);
4632N/A
4632N/A CFRelease(topKey);
4632N/A if (topValue) CFRelease(topValue);
4632N/A return result;
4632N/A}
4632N/A
4632N/A
4632N/A// Create a new tree that would store path in the given file.
4632N/A// Only the root of the tree is created, not all of the links leading to path.
4632N/A// returns NULL on error
4632N/Astatic void createTreeForPath(CFStringRef path, CFStringRef name,
4632N/A CFStringRef user, CFStringRef host,
4632N/A CFStringRef *outTopKey,
4632N/A CFMutableDictionaryRef *outTopValue)
4632N/A{
4632N/A *outTopKey = NULL;
4632N/A *outTopValue = NULL;
4632N/A
4632N/A // if name is "com.apple.java.util.prefs" then create tree "/"
4632N/A // else create tree "/foo/bar/baz/"
4632N/A // "com.apple.java.util.prefs.plist" is also in MacOSXPreferences.java
4632N/A if (CFEqual(name, CFSTR("com.apple.java.util.prefs"))) {
4632N/A *outTopKey = CFSTR("/");
4632N/A *outTopValue = createEmptyNode();
4632N/A } else {
4632N/A CFStringRef prefix = copyFirstThreeComponentsOf(path);
4632N/A if (prefix) {
4632N/A *outTopKey = prefix;
4632N/A *outTopValue = createEmptyNode();
4632N/A }
4632N/A }
4632N/A}
4632N/A
4632N/A
4632N/A// Return a mutable copy of the tree containing path and the dict for
4632N/A// path itself. *outTopKey and *outTopValue can be used to write the
4632N/A// modified tree back to the prefs file.
4632N/A// *outTopKey and *outTopValue must be released iff the actual return
4632N/A// value is not NULL.
4632N/Astatic CFMutableDictionaryRef
4632N/AcopyMutableNode(CFStringRef path, CFStringRef name,
4632N/A CFStringRef user, CFStringRef host,
4632N/A CFStringRef *outTopKey,
4632N/A CFMutableDictionaryRef *outTopValue)
4632N/A{
4632N/A CFStringRef topKey = NULL;
4632N/A CFDictionaryRef oldTopValue = NULL;
4632N/A CFMutableDictionaryRef topValue;
4632N/A CFMutableDictionaryRef result = NULL;
4632N/A CFMutableStringRef p;
4632N/A
4632N/A if (outTopKey) *outTopKey = NULL;
4632N/A if (outTopValue) *outTopValue = NULL;
4632N/A
4632N/A copyTreeForPath(path, name, user, host, &topKey, &oldTopValue);
4632N/A if (!topKey) {
4632N/A createTreeForPath(path, name, user, host, &topKey, &topValue);
4632N/A } else {
4632N/A topValue = (CFMutableDictionaryRef)
4632N/A CFPropertyListCreateDeepCopy(NULL, (CFPropertyListRef)oldTopValue,
4632N/A kCFPropertyListMutableContainers);
4632N/A }
4632N/A if (!topValue) goto badtopValue;
4632N/A
4632N/A p = CFStringCreateMutableCopy(NULL, 0, path);
4632N/A if (!p) goto badp;
4632N/A CFStringDelete(p, CFRangeMake(0, CFStringGetLength(topKey)));
4632N/A result = topValue;
4632N/A
4632N/A while (CFStringGetLength(p) > 0) {
4632N/A CFMutableDictionaryRef child;
4632N/A CFStringRef part = NULL;
4632N/A CFRange slashRange = CFStringFind(p, CFSTR("/"), 0);
4632N/A // guaranteed to succeed because path must end in '/'
4632N/A CFRange partRange = CFRangeMake(0, slashRange.location + 1);
4632N/A part = CFStringCreateWithSubstring(NULL, p, partRange);
4632N/A if (!part) { result = NULL; break; }
4632N/A CFStringDelete(p, partRange);
4632N/A
4632N/A child = (CFMutableDictionaryRef)CFDictionaryGetValue(result, part);
4632N/A if (child && CFGetTypeID(child) == CFDictionaryGetTypeID()) {
4632N/A // continue search
4632N/A result = child;
4632N/A } else {
4632N/A // didn't find target node - add it and continue
4632N/A child = createEmptyNode();
4632N/A if (!child) { CFRelease(part); result = NULL; break; }
4632N/A CFDictionaryAddValue(result, part, child);
4632N/A result = child;
4632N/A }
4632N/A CFRelease(part);
4632N/A }
4632N/A
4632N/A if (result) {
4632N/A *outTopKey = (CFStringRef)CFRetain(topKey);
4632N/A *outTopValue = (CFMutableDictionaryRef)CFRetain(topValue);
4632N/A CFRetain(result);
4632N/A }
4632N/A
4632N/A CFRelease(p);
4632N/A badp:
4632N/A CFRelease(topValue);
4632N/A badtopValue:
4632N/A if (topKey) CFRelease(topKey);
4632N/A if (oldTopValue) CFRelease(oldTopValue);
4632N/A return result;
4632N/A}
4632N/A
4632N/A
4632N/AJNIEXPORT jboolean JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_addNode
4632N/A(JNIEnv *env, jobject klass, jobject jpath,
4632N/A jobject jname, jlong juser, jlong jhost)
4632N/A{
4632N/A CFStringRef path = toCF(env, jpath);
4632N/A CFStringRef name = toCF(env, jname);
4632N/A CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
4632N/A CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
4632N/A CFDictionaryRef node = NULL;
4632N/A jboolean neededNewNode = false;
4632N/A
4632N/A if (!path || !name) goto badparams;
4632N/A
4632N/A node = copyNodeIfPresent(path, name, user, host);
4632N/A
4632N/A if (node) {
4632N/A neededNewNode = false;
4632N/A CFRelease(node);
4632N/A } else {
4632N/A CFStringRef topKey = NULL;
4632N/A CFMutableDictionaryRef topValue = NULL;
4632N/A
4632N/A neededNewNode = true;
4632N/A
4632N/A // copyMutableNode creates the node if necessary
4632N/A node = copyMutableNode(path, name, user, host, &topKey, &topValue);
4632N/A throwIfNull(node, "copyMutableNode failed");
4632N/A
4632N/A CFPreferencesSetValue(topKey, topValue, name, user, host);
4632N/A
4632N/A CFRelease(node);
4632N/A if (topKey) CFRelease(topKey);
4632N/A if (topValue) CFRelease(topValue);
4632N/A }
4632N/A
4632N/A badnode:
4632N/A badparams:
4632N/A if (path) CFRelease(path);
4632N/A if (name) CFRelease(name);
4632N/A
4632N/A return neededNewNode;
4632N/A}
4632N/A
4632N/A
4632N/AJNIEXPORT void JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_removeNode
4632N/A(JNIEnv *env, jobject klass, jobject jpath,
4632N/A jobject jname, jlong juser, jlong jhost)
4632N/A{
4632N/A CFStringRef path = toCF(env, jpath);
4632N/A CFStringRef name = toCF(env, jname);
4632N/A CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
4632N/A CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
4632N/A CFStringRef parentName;
4632N/A CFStringRef childName;
4632N/A CFDictionaryRef constParent;
4632N/A
4632N/A if (!path || !name) goto badparams;
4632N/A
4632N/A parentName = copyParentOf(path);
4632N/A throwIfNull(parentName, "copyParentOf failed");
4632N/A childName = copyChildOf(path);
4632N/A throwIfNull(childName, "copyChildOf failed");
4632N/A
4632N/A // root node is not allowed to be removed, so parentName is never empty
4632N/A
4632N/A constParent = copyNodeIfPresent(parentName, name, user, host);
4632N/A if (constParent && CFDictionaryContainsKey(constParent, childName)) {
4632N/A CFStringRef topKey;
4632N/A CFMutableDictionaryRef topValue;
4632N/A CFMutableDictionaryRef parent;
4632N/A
4632N/A parent = copyMutableNode(parentName, name, user, host,
4632N/A &topKey, &topValue);
4632N/A throwIfNull(parent, "copyMutableNode failed");
4632N/A
4632N/A CFDictionaryRemoveValue(parent, childName);
4632N/A CFPreferencesSetValue(topKey, topValue, name, user, host);
4632N/A
4632N/A CFRelease(parent);
4632N/A if (topKey) CFRelease(topKey);
4632N/A if (topValue) CFRelease(topValue);
4632N/A } else {
4632N/A // might be trying to remove the root itself in a non-root file
4632N/A CFStringRef topKey;
4632N/A CFDictionaryRef topValue;
4632N/A copyTreeForPath(path, name, user, host, &topKey, &topValue);
4632N/A if (topKey) {
4632N/A if (CFEqual(topKey, path)) {
4632N/A CFPreferencesSetValue(topKey, NULL, name, user, host);
4632N/A }
4632N/A
4632N/A if (topKey) CFRelease(topKey);
4632N/A if (topValue) CFRelease(topValue);
4632N/A }
4632N/A }
4632N/A
4632N/A
4632N/A badparent:
4632N/A if (constParent) CFRelease(constParent);
4632N/A CFRelease(childName);
4632N/A badchildName:
4632N/A CFRelease(parentName);
4632N/A badparentName:
4632N/A badparams:
4632N/A if (path) CFRelease(path);
4632N/A if (name) CFRelease(name);
4632N/A}
4632N/A
4632N/A
4632N/A// child must end with '/'
5286N/AJNIEXPORT Boolean JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_addChildToNode
4632N/A(JNIEnv *env, jobject klass, jobject jpath, jobject jchild,
4632N/A jobject jname, jlong juser, jlong jhost)
4632N/A{
4632N/A // like addNode, but can put a three-level-deep dict into the root file
4632N/A CFStringRef path = toCF(env, jpath);
4632N/A CFStringRef child = toCF(env, jchild);
4632N/A CFStringRef name = toCF(env, jname);
4632N/A CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
4632N/A CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
4632N/A CFMutableDictionaryRef parent;
4632N/A CFDictionaryRef node;
4632N/A CFStringRef topKey;
4632N/A CFMutableDictionaryRef topValue;
5286N/A Boolean beforeAdd = false;
4632N/A
4632N/A if (!path || !child || !name) goto badparams;
4632N/A
4632N/A node = createEmptyNode();
4632N/A throwIfNull(node, "createEmptyNode failed");
4632N/A
4632N/A // copyMutableNode creates the node if necessary
4632N/A parent = copyMutableNode(path, name, user, host, &topKey, &topValue);
4632N/A throwIfNull(parent, "copyMutableNode failed");
5286N/A beforeAdd = CFDictionaryContainsKey(parent, child);
4632N/A CFDictionaryAddValue(parent, child, node);
5286N/A if (!beforeAdd)
5286N/A beforeAdd = CFDictionaryContainsKey(parent, child);
5286N/A else
5286N/A beforeAdd = false;
4632N/A CFPreferencesSetValue(topKey, topValue, name, user, host);
4632N/A
4632N/A CFRelease(parent);
4632N/A if (topKey) CFRelease(topKey);
4632N/A if (topValue) CFRelease(topValue);
4632N/A badparent:
4632N/A CFRelease(node);
4632N/A badnode:
4632N/A badparams:
4632N/A if (path) CFRelease(path);
4632N/A if (child) CFRelease(child);
4632N/A if (name) CFRelease(name);
5286N/A return beforeAdd;
4632N/A}
4632N/A
4632N/A
4632N/AJNIEXPORT void JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_removeChildFromNode
4632N/A(JNIEnv *env, jobject klass, jobject jpath, jobject jchild,
4632N/A jobject jname, jlong juser, jlong jhost)
4632N/A{
4632N/A CFStringRef path = toCF(env, jpath);
4632N/A CFStringRef child = toCF(env, jchild);
4632N/A CFStringRef name = toCF(env, jname);
4632N/A CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
4632N/A CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
4632N/A CFDictionaryRef constParent;
4632N/A
4632N/A if (!path || !child || !name) goto badparams;
4632N/A
4632N/A constParent = copyNodeIfPresent(path, name, user, host);
4632N/A if (constParent && CFDictionaryContainsKey(constParent, child)) {
4632N/A CFStringRef topKey;
4632N/A CFMutableDictionaryRef topValue;
4632N/A CFMutableDictionaryRef parent;
4632N/A
4632N/A parent = copyMutableNode(path, name, user, host, &topKey, &topValue);
4632N/A throwIfNull(parent, "copyMutableNode failed");
4632N/A
4632N/A CFDictionaryRemoveValue(parent, child);
4632N/A CFPreferencesSetValue(topKey, topValue, name, user, host);
4632N/A
4632N/A CFRelease(parent);
4632N/A if (topKey) CFRelease(topKey);
4632N/A if (topValue) CFRelease(topValue);
4632N/A }
4632N/A
4632N/A badparent:
4632N/A if (constParent) CFRelease(constParent);
4632N/A badparams:
4632N/A if (path) CFRelease(path);
4632N/A if (child) CFRelease(child);
4632N/A if (name) CFRelease(name);
4632N/A}
4632N/A
4632N/A
4632N/A
4632N/AJNIEXPORT void JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_addKeyToNode
4632N/A(JNIEnv *env, jobject klass, jobject jpath, jobject jkey, jobject jvalue,
4632N/A jobject jname, jlong juser, jlong jhost)
4632N/A{
4632N/A CFStringRef path = toCF(env, jpath);
4632N/A CFStringRef key = toCF(env, jkey);
4632N/A CFStringRef value = toCF(env, jvalue);
4632N/A CFStringRef name = toCF(env, jname);
4632N/A CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
4632N/A CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
4632N/A CFMutableDictionaryRef node = NULL;
4632N/A CFStringRef topKey;
4632N/A CFMutableDictionaryRef topValue;
4632N/A
4632N/A if (!path || !key || !value || !name) goto badparams;
4632N/A
4632N/A // fixme optimization: check whether old value and new value are identical
4632N/A node = copyMutableNode(path, name, user, host, &topKey, &topValue);
4632N/A throwIfNull(node, "copyMutableNode failed");
4632N/A
4632N/A CFDictionarySetValue(node, key, value);
4632N/A CFPreferencesSetValue(topKey, topValue, name, user, host);
4632N/A
4632N/A CFRelease(node);
4632N/A if (topKey) CFRelease(topKey);
4632N/A if (topValue) CFRelease(topValue);
4632N/A
4632N/A badnode:
4632N/A badparams:
4632N/A if (path) CFRelease(path);
4632N/A if (key) CFRelease(key);
4632N/A if (value) CFRelease(value);
4632N/A if (name) CFRelease(name);
4632N/A}
4632N/A
4632N/A
4632N/AJNIEXPORT void JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_removeKeyFromNode
4632N/A(JNIEnv *env, jobject klass, jobject jpath, jobject jkey,
4632N/A jobject jname, jlong juser, jlong jhost)
4632N/A{
4632N/A CFStringRef path = toCF(env, jpath);
4632N/A CFStringRef key = toCF(env, jkey);
4632N/A CFStringRef name = toCF(env, jname);
4632N/A CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
4632N/A CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
4632N/A CFDictionaryRef constNode;
4632N/A
4632N/A if (!path || !key || !name) goto badparams;
4632N/A
4632N/A constNode = copyNodeIfPresent(path, name, user, host);
4632N/A if (constNode && CFDictionaryContainsKey(constNode, key)) {
4632N/A CFStringRef topKey;
4632N/A CFMutableDictionaryRef topValue;
4632N/A CFMutableDictionaryRef node;
4632N/A
4632N/A node = copyMutableNode(path, name, user, host, &topKey, &topValue);
4632N/A throwIfNull(node, "copyMutableNode failed");
4632N/A
4632N/A CFDictionaryRemoveValue(node, key);
4632N/A CFPreferencesSetValue(topKey, topValue, name, user, host);
4632N/A
4632N/A CFRelease(node);
4632N/A if (topKey) CFRelease(topKey);
4632N/A if (topValue) CFRelease(topValue);
4632N/A }
4632N/A
4632N/A badnode:
4632N/A if (constNode) CFRelease(constNode);
4632N/A badparams:
4632N/A if (path) CFRelease(path);
4632N/A if (key) CFRelease(key);
4632N/A if (name) CFRelease(name);
4632N/A}
4632N/A
4632N/A
4632N/A// path must end in '/'
4632N/AJNIEXPORT jstring JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_getKeyFromNode
4632N/A(JNIEnv *env, jobject klass, jobject jpath, jobject jkey,
4632N/A jobject jname, jlong juser, jlong jhost)
4632N/A{
4632N/A CFStringRef path = toCF(env, jpath);
4632N/A CFStringRef key = toCF(env, jkey);
4632N/A CFStringRef name = toCF(env, jname);
4632N/A CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
4632N/A CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
4632N/A CFPropertyListRef value;
4632N/A CFDictionaryRef node;
4632N/A jstring result = NULL;
4632N/A
4632N/A if (!path || !key || !name) goto badparams;
4632N/A
4632N/A node = copyNodeIfPresent(path, name, user, host);
4632N/A if (node) {
4632N/A value = (CFPropertyListRef)CFDictionaryGetValue(node, key);
4632N/A if (!value) {
4632N/A // key doesn't exist, or other error - no Java errors available
4632N/A result = NULL;
4632N/A } else {
4632N/A CFStringRef cfString = copyToCFString(env, value);
4632N/A if ((*env)->ExceptionOccurred(env)) {
4632N/A // memory error in copyToCFString
4632N/A result = NULL;
4632N/A } else if (cfString == NULL) {
4632N/A // bogus value type in prefs file - no Java errors available
4632N/A result = NULL;
4632N/A } else {
4632N/A // good cfString
4632N/A result = toJavaString(env, cfString);
4632N/A CFRelease(cfString);
4632N/A }
4632N/A }
4632N/A CFRelease(node);
4632N/A }
4632N/A
4632N/A badparams:
4632N/A if (path) CFRelease(path);
4632N/A if (key) CFRelease(key);
4632N/A if (name) CFRelease(name);
4632N/A
4632N/A return result;
4632N/A}
4632N/A
4632N/A
4632N/Atypedef struct {
4632N/A jarray result;
4632N/A JNIEnv *env;
4632N/A CFIndex used;
4632N/A Boolean allowSlash;
4632N/A} BuildJavaArrayArgs;
4632N/A
4632N/A// CFDictionary applier function that builds an array of Java strings
4632N/A// from a CFDictionary of CFPropertyListRefs.
4632N/A// If args->allowSlash, only strings that end in '/' are added to the array,
4632N/A// with the slash removed. Otherwise, only strings that do not end in '/'
4632N/A// are added.
4632N/A// args->result must already exist and be large enough to hold all
4632N/A// strings from the dictionary.
4632N/A// After complete application, args->result may not be full because
4632N/A// some of the dictionary values weren't convertible to string. In
4632N/A// this case, args->used will be the count of used elements.
4632N/Astatic void BuildJavaArrayFn(const void *key, const void *value, void *context)
4632N/A{
4632N/A BuildJavaArrayArgs *args = (BuildJavaArrayArgs *)context;
4632N/A CFPropertyListRef propkey = (CFPropertyListRef)key;
4632N/A CFStringRef cfString = NULL;
4632N/A JNIEnv *env = args->env;
4632N/A
4632N/A if ((*env)->ExceptionOccurred(env)) return; // already failed
4632N/A
4632N/A cfString = copyToCFString(env, propkey);
4632N/A if ((*env)->ExceptionOccurred(env)) {
4632N/A // memory error in copyToCFString
4632N/A } else if (!cfString) {
4632N/A // bogus value type in prefs file - no Java errors available
4632N/A } else if (args->allowSlash != CFStringHasSuffix(cfString, CFSTR("/"))) {
4632N/A // wrong suffix - ignore
4632N/A } else {
4632N/A // good cfString
4632N/A jstring javaString;
4632N/A if (args->allowSlash) {
4632N/A CFRange range = CFRangeMake(0, CFStringGetLength(cfString) - 1);
4632N/A CFStringRef s = CFStringCreateWithSubstring(NULL, cfString, range);
4632N/A CFRelease(cfString);
4632N/A cfString = s;
4632N/A }
4632N/A if (CFStringGetLength(cfString) <= 0) goto bad; // ignore empty
4632N/A javaString = toJavaString(env, cfString);
4632N/A if ((*env)->ExceptionOccurred(env)) goto bad;
4632N/A (*env)->SetObjectArrayElement(env, args->result,args->used,javaString);
4632N/A if ((*env)->ExceptionOccurred(env)) goto bad;
4632N/A args->used++;
4632N/A }
4632N/A
4632N/A bad:
4632N/A if (cfString) CFRelease(cfString);
4632N/A}
4632N/A
4632N/A
4632N/Astatic jarray getStringsForNode(JNIEnv *env, jobject klass, jobject jpath,
4632N/A jobject jname, jlong juser, jlong jhost,
4632N/A Boolean allowSlash)
4632N/A{
4632N/A CFStringRef path = toCF(env, jpath);
4632N/A CFStringRef name = toCF(env, jname);
4632N/A CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
4632N/A CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
4632N/A CFDictionaryRef node;
4632N/A jarray result = NULL;
4632N/A CFIndex count;
4632N/A
4632N/A if (!path || !name) goto badparams;
4632N/A
4632N/A node = copyNodeIfPresent(path, name, user, host);
4632N/A if (!node) {
4632N/A result = createJavaStringArray(env, 0);
4632N/A } else {
4632N/A count = CFDictionaryGetCount(node);
4632N/A result = createJavaStringArray(env, count);
4632N/A if (result) {
4632N/A BuildJavaArrayArgs args;
4632N/A args.result = result;
4632N/A args.env = env;
4632N/A args.used = 0;
4632N/A args.allowSlash = allowSlash;
4632N/A CFDictionaryApplyFunction(node, BuildJavaArrayFn, &args);
4632N/A if (!(*env)->ExceptionOccurred(env)) {
4632N/A // array construction succeeded
4632N/A if (args.used < count) {
4632N/A // finished array is smaller than expected.
4632N/A // Make a new array of precisely the right size.
4632N/A jarray newresult = createJavaStringArray(env, args.used);
4632N/A if (newresult) {
4632N/A JVM_ArrayCopy(env,0, result,0, newresult,0, args.used);
4632N/A result = newresult;
4632N/A }
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A CFRelease(node);
4632N/A }
4632N/A
4632N/A badparams:
4632N/A if (path) CFRelease(path);
4632N/A if (name) CFRelease(name);
4632N/A
4632N/A return result;
4632N/A}
4632N/A
4632N/A
4632N/AJNIEXPORT jarray JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_getKeysForNode
4632N/A(JNIEnv *env, jobject klass, jobject jpath,
4632N/A jobject jname, jlong juser, jlong jhost)
4632N/A{
4632N/A return getStringsForNode(env, klass, jpath, jname, juser, jhost, false);
4632N/A}
4632N/A
4632N/AJNIEXPORT jarray JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_getChildrenForNode
4632N/A(JNIEnv *env, jobject klass, jobject jpath,
4632N/A jobject jname, jlong juser, jlong jhost)
4632N/A{
4632N/A return getStringsForNode(env, klass, jpath, jname, juser, jhost, true);
4632N/A}
4632N/A
4632N/A
4632N/A// Returns false on error instead of throwing.
4632N/AJNIEXPORT jboolean JNICALL
4632N/AJava_java_util_prefs_MacOSXPreferencesFile_synchronize
4632N/A(JNIEnv *env, jobject klass,
4632N/A jstring jname, jlong juser, jlong jhost)
4632N/A{
4632N/A CFStringRef name = toCF(env, jname);
4632N/A CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
4632N/A CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
4632N/A jboolean result = 0;
4632N/A
4632N/A if (name) {
4632N/A result = CFPreferencesSynchronize(name, user, host);
4632N/A CFRelease(name);
4632N/A }
4632N/A
4632N/A return result;
4632N/A}