Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/native_NOTIOS/java/util/MacOSXPreferencesFile.m
46972 views
/*1* Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26Hierarchical storage layout:2728<dict>29<key>/</key>30<dict>31<key>foo</key>32<string>/foo's value</string>33<key>foo/</key>34<dict>35<key>bar</key>36<string>/foo/bar's value</string>37</dict>38</dict>39</dict>4041Java pref nodes are stored in several different files. Pref nodes42with at least three components in the node name (e.g. /com/MyCompany/MyApp/)43are stored in a CF prefs file with the first three components as the name.44This way, all preferences for MyApp end up in com.MyCompany.MyApp.plist .45Pref nodes with shorter names are stored in com.apple.java.util.prefs.plist4647The filesystem is assumed to be case-insensitive (like HFS+).48Java pref node names are case-sensitive. If two pref node names differ49only in case, they may end up in the same pref file. This is ok50because the CF keys identifying the node span the entire absolute path51to the node and are case-sensitive.5253Java node names may contain '.' . When mapping to the CF file name,54these dots are left as-is, even though '/' is mapped to '.' .55This is ok because the CF key contains the correct node name.56*/57585960#include <CoreFoundation/CoreFoundation.h>6162#include "jni_util.h"63#include "jlong.h"64#include "jvm.h"656667// Throw an OutOfMemoryError with the given message.68static void throwOutOfMemoryError(JNIEnv *env, const char *msg)69{70static jclass exceptionClass = NULL;71jclass c;7273(*env)->ExceptionClear(env); // If an exception is pending, clear it before74// calling FindClass() and/or ThrowNew().75if (exceptionClass) {76c = exceptionClass;77} else {78c = (*env)->FindClass(env, "java/lang/OutOfMemoryError");79if ((*env)->ExceptionOccurred(env)) return;80exceptionClass = (*env)->NewGlobalRef(env, c);81}8283(*env)->ThrowNew(env, c, msg);84}858687// throwIfNull macro88// If var is NULL, throw an OutOfMemoryError and goto badvar.89// var must be a variable. env must be the current JNIEnv.90// fixme throw BackingStoreExceptions sometimes?91#define throwIfNull(var, msg) \92do { \93if (var == NULL) { \94throwOutOfMemoryError(env, msg); \95goto bad##var; \96} \97} while (0)9899100// Converts CFNumber, CFBoolean, CFString to CFString101// returns NULL if value is of some other type102// throws and returns NULL on memory error103// result must be released (even if value was already a CFStringRef)104// value must not be null105static CFStringRef copyToCFString(JNIEnv *env, CFTypeRef value)106{107CFStringRef result;108CFTypeID type;109110type = CFGetTypeID(value);111112if (type == CFStringGetTypeID()) {113result = (CFStringRef)CFRetain(value);114}115else if (type == CFBooleanGetTypeID()) {116// Java Preferences API expects "true" and "false" for boolean values.117result = CFStringCreateCopy(NULL, (value == kCFBooleanTrue) ? CFSTR("true") : CFSTR("false"));118throwIfNull(result, "copyToCFString failed");119}120else if (type == CFNumberGetTypeID()) {121CFNumberRef number = (CFNumberRef) value;122if (CFNumberIsFloatType(number)) {123double d;124CFNumberGetValue(number, kCFNumberDoubleType, &d);125result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%g"), d);126throwIfNull(result, "copyToCFString failed");127}128else {129long l;130CFNumberGetValue(number, kCFNumberLongType, &l);131result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%ld"), l);132throwIfNull(result, "copyToCFString failed");133}134}135else {136// unknown type - return NULL137result = NULL;138}139140badresult:141return result;142}143144145// Create a Java string from the given CF string.146// returns NULL if cfString is NULL147// throws and returns NULL on memory error148static jstring toJavaString(JNIEnv *env, CFStringRef cfString)149{150if (cfString == NULL) {151return NULL;152} else {153jstring javaString = NULL;154155CFIndex length = CFStringGetLength(cfString);156const UniChar *constchars = CFStringGetCharactersPtr(cfString);157if (constchars) {158javaString = (*env)->NewString(env, constchars, length);159} else {160UniChar *chars = malloc(length * sizeof(UniChar));161throwIfNull(chars, "toJavaString failed");162CFStringGetCharacters(cfString, CFRangeMake(0, length), chars);163javaString = (*env)->NewString(env, chars, length);164free(chars);165}166badchars:167return javaString;168}169}170171172173// Create a CF string from the given Java string.174// returns NULL if javaString is NULL175// throws and returns NULL on memory error176static CFStringRef toCF(JNIEnv *env, jstring javaString)177{178if (javaString == NULL) {179return NULL;180} else {181CFStringRef result = NULL;182jsize length = (*env)->GetStringLength(env, javaString);183const jchar *chars = (*env)->GetStringChars(env, javaString, NULL);184throwIfNull(chars, "toCF failed");185result =186CFStringCreateWithCharacters(NULL, (const UniChar *)chars, length);187(*env)->ReleaseStringChars(env, javaString, chars);188throwIfNull(result, "toCF failed");189badchars:190badresult:191return result;192}193}194195196// Create an empty Java string array of the given size.197// Throws and returns NULL on error.198static jarray createJavaStringArray(JNIEnv *env, CFIndex count)199{200static jclass stringClass = NULL;201jclass c;202203if (stringClass) {204c = stringClass;205} else {206c = (*env)->FindClass(env, "java/lang/String");207if ((*env)->ExceptionOccurred(env)) return NULL;208stringClass = (*env)->NewGlobalRef(env, c);209}210211return (*env)->NewObjectArray(env, count, c, NULL); // AWT_THREADING Safe (known object)212}213214215// Java accessors for CF constants.216JNIEXPORT jlong JNICALL217Java_java_util_prefs_MacOSXPreferencesFile_currentUser(JNIEnv *env,218jobject klass)219{220return ptr_to_jlong(kCFPreferencesCurrentUser);221}222223JNIEXPORT jlong JNICALL224Java_java_util_prefs_MacOSXPreferencesFile_anyUser(JNIEnv *env, jobject klass)225{226return ptr_to_jlong(kCFPreferencesAnyUser);227}228229JNIEXPORT jlong JNICALL230Java_java_util_prefs_MacOSXPreferencesFile_currentHost(JNIEnv *env,231jobject klass)232{233return ptr_to_jlong(kCFPreferencesCurrentHost);234}235236JNIEXPORT jlong JNICALL237Java_java_util_prefs_MacOSXPreferencesFile_anyHost(JNIEnv *env, jobject klass)238{239return ptr_to_jlong(kCFPreferencesAnyHost);240}241242243// Create an empty node.244// Does not store the node in any prefs file.245// returns NULL on memory error246static CFMutableDictionaryRef createEmptyNode(void)247{248return CFDictionaryCreateMutable(NULL, 0,249&kCFTypeDictionaryKeyCallBacks,250&kCFTypeDictionaryValueCallBacks);251}252253254// Create a string that consists of path minus its last component.255// path must end with '/'256// The result will end in '/' (unless path itself is '/')257static CFStringRef copyParentOf(CFStringRef path)258{259CFRange searchRange;260CFRange slashRange;261CFRange parentRange;262Boolean found;263264searchRange = CFRangeMake(0, CFStringGetLength(path) - 1);265found = CFStringFindWithOptions(path, CFSTR("/"), searchRange,266kCFCompareBackwards, &slashRange);267if (!found) return CFSTR("");268parentRange = CFRangeMake(0, slashRange.location + 1); // include '/'269return CFStringCreateWithSubstring(NULL, path, parentRange);270}271272273// Create a string that consists of path's last component.274// path must end with '/'275// The result will end in '/'.276// The result will not start with '/' (unless path itself is '/')277static CFStringRef copyChildOf(CFStringRef path)278{279CFRange searchRange;280CFRange slashRange;281CFRange childRange;282Boolean found;283CFIndex length = CFStringGetLength(path);284285searchRange = CFRangeMake(0, length - 1);286found = CFStringFindWithOptions(path, CFSTR("/"), searchRange,287kCFCompareBackwards, &slashRange);288if (!found) return CFSTR("");289childRange = CFRangeMake(slashRange.location + 1,290length - slashRange.location - 1); // skip '/'291return CFStringCreateWithSubstring(NULL, path, childRange);292}293294295// Return the first three components of path, with leading and trailing '/'.296// If path does not have three components, return NULL.297// path must begin and end in '/'298static CFStringRef copyFirstThreeComponentsOf(CFStringRef path)299{300CFRange searchRange;301CFRange slashRange;302CFRange prefixRange;303CFStringRef prefix;304Boolean found;305CFIndex length = CFStringGetLength(path);306307searchRange = CFRangeMake(1, length - 1); // skip leading '/'308found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,309&slashRange);310if (!found) return NULL; // no second slash!311312searchRange = CFRangeMake(slashRange.location + 1,313length - slashRange.location - 1);314found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,315&slashRange);316if (!found) return NULL; // no third slash!317318searchRange = CFRangeMake(slashRange.location + 1,319length - slashRange.location - 1);320found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,321&slashRange);322if (!found) return NULL; // no fourth slash!323324prefixRange = CFRangeMake(0, slashRange.location + 1); // keep last '/'325prefix = CFStringCreateWithSubstring(NULL, path, prefixRange);326327return prefix;328}329330331// Copy the CFPreferences key and value at the base of path's tree.332// path must end in '/'333// topKey or topValue may be NULL334// Returns NULL on error or if there is no tree for path in this file.335static void copyTreeForPath(CFStringRef path, CFStringRef name,336CFStringRef user, CFStringRef host,337CFStringRef *topKey, CFDictionaryRef *topValue)338{339CFStringRef key;340CFPropertyListRef value;341342if (topKey) *topKey = NULL;343if (topValue) *topValue = NULL;344345if (CFEqual(name, CFSTR("com.apple.java.util.prefs"))) {346// Top-level file. Only key "/" is an acceptable root.347key = (CFStringRef) CFRetain(CFSTR("/"));348} else {349// Second-level file. Key must be the first three components of path.350key = copyFirstThreeComponentsOf(path);351if (!key) return;352}353354value = CFPreferencesCopyValue(key, name, user, host);355if (value) {356if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {357// (key, value) is acceptable358if (topKey) *topKey = (CFStringRef)CFRetain(key);359if (topValue) *topValue = (CFDictionaryRef)CFRetain(value);360}361CFRelease(value);362}363CFRelease(key);364}365366367// Find the node for path in the given tree.368// Returns NULL on error or if path doesn't have a node in this tree.369// path must end in '/'370static CFDictionaryRef copyNodeInTree(CFStringRef path, CFStringRef topKey,371CFDictionaryRef topValue)372{373CFMutableStringRef p;374CFDictionaryRef result = NULL;375376p = CFStringCreateMutableCopy(NULL, 0, path);377if (!p) return NULL;378CFStringDelete(p, CFRangeMake(0, CFStringGetLength(topKey)));379result = topValue;380381while (CFStringGetLength(p) > 0) {382CFDictionaryRef child;383CFStringRef part = NULL;384CFRange slashRange = CFStringFind(p, CFSTR("/"), 0);385// guaranteed to succeed because path must end in '/'386CFRange partRange = CFRangeMake(0, slashRange.location + 1);387part = CFStringCreateWithSubstring(NULL, p, partRange);388if (!part) { result = NULL; break; }389CFStringDelete(p, partRange);390391child = CFDictionaryGetValue(result, part);392CFRelease(part);393if (child && CFGetTypeID(child) == CFDictionaryGetTypeID()) {394// continue search395result = child;396} else {397// didn't find target node398result = NULL;399break;400}401}402403CFRelease(p);404if (result) return (CFDictionaryRef)CFRetain(result);405else return NULL;406}407408409// Return a retained copy of the node at path from the given file.410// path must end in '/'411// returns NULL if node doesn't exist.412// returns NULL if the value for key "path" isn't a valid node.413static CFDictionaryRef copyNodeIfPresent(CFStringRef path, CFStringRef name,414CFStringRef user, CFStringRef host)415{416CFStringRef topKey;417CFDictionaryRef topValue;418CFDictionaryRef result;419420copyTreeForPath(path, name, user, host, &topKey, &topValue);421if (!topKey) return NULL;422423result = copyNodeInTree(path, topKey, topValue);424425CFRelease(topKey);426if (topValue) CFRelease(topValue);427return result;428}429430431// Create a new tree that would store path in the given file.432// Only the root of the tree is created, not all of the links leading to path.433// returns NULL on error434static void createTreeForPath(CFStringRef path, CFStringRef name,435CFStringRef user, CFStringRef host,436CFStringRef *outTopKey,437CFMutableDictionaryRef *outTopValue)438{439*outTopKey = NULL;440*outTopValue = NULL;441442// if name is "com.apple.java.util.prefs" then create tree "/"443// else create tree "/foo/bar/baz/"444// "com.apple.java.util.prefs.plist" is also in MacOSXPreferences.java445if (CFEqual(name, CFSTR("com.apple.java.util.prefs"))) {446*outTopKey = CFSTR("/");447*outTopValue = createEmptyNode();448} else {449CFStringRef prefix = copyFirstThreeComponentsOf(path);450if (prefix) {451*outTopKey = prefix;452*outTopValue = createEmptyNode();453}454}455}456457458// Return a mutable copy of the tree containing path and the dict for459// path itself. *outTopKey and *outTopValue can be used to write the460// modified tree back to the prefs file.461// *outTopKey and *outTopValue must be released iff the actual return462// value is not NULL.463static CFMutableDictionaryRef464copyMutableNode(CFStringRef path, CFStringRef name,465CFStringRef user, CFStringRef host,466CFStringRef *outTopKey,467CFMutableDictionaryRef *outTopValue)468{469CFStringRef topKey = NULL;470CFDictionaryRef oldTopValue = NULL;471CFMutableDictionaryRef topValue;472CFMutableDictionaryRef result = NULL;473CFMutableStringRef p;474475if (outTopKey) *outTopKey = NULL;476if (outTopValue) *outTopValue = NULL;477478copyTreeForPath(path, name, user, host, &topKey, &oldTopValue);479if (!topKey) {480createTreeForPath(path, name, user, host, &topKey, &topValue);481} else {482topValue = (CFMutableDictionaryRef)483CFPropertyListCreateDeepCopy(NULL, (CFPropertyListRef)oldTopValue,484kCFPropertyListMutableContainers);485}486if (!topValue) goto badtopValue;487488p = CFStringCreateMutableCopy(NULL, 0, path);489if (!p) goto badp;490CFStringDelete(p, CFRangeMake(0, CFStringGetLength(topKey)));491result = topValue;492493while (CFStringGetLength(p) > 0) {494CFMutableDictionaryRef child;495CFStringRef part = NULL;496CFRange slashRange = CFStringFind(p, CFSTR("/"), 0);497// guaranteed to succeed because path must end in '/'498CFRange partRange = CFRangeMake(0, slashRange.location + 1);499part = CFStringCreateWithSubstring(NULL, p, partRange);500if (!part) { result = NULL; break; }501CFStringDelete(p, partRange);502503child = (CFMutableDictionaryRef)CFDictionaryGetValue(result, part);504if (child && CFGetTypeID(child) == CFDictionaryGetTypeID()) {505// continue search506result = child;507} else {508// didn't find target node - add it and continue509child = createEmptyNode();510if (!child) { CFRelease(part); result = NULL; break; }511CFDictionaryAddValue(result, part, child);512result = child;513}514CFRelease(part);515}516517if (result) {518*outTopKey = (CFStringRef)CFRetain(topKey);519*outTopValue = (CFMutableDictionaryRef)CFRetain(topValue);520CFRetain(result);521}522523CFRelease(p);524badp:525CFRelease(topValue);526badtopValue:527if (topKey) CFRelease(topKey);528if (oldTopValue) CFRelease(oldTopValue);529return result;530}531532533JNIEXPORT jboolean JNICALL534Java_java_util_prefs_MacOSXPreferencesFile_addNode535(JNIEnv *env, jobject klass, jobject jpath,536jobject jname, jlong juser, jlong jhost)537{538CFStringRef path = NULL;539CFStringRef name = NULL;540541path = toCF(env, jpath);542if (path != NULL) {543name = toCF(env, jname);544}545CFStringRef user = (CFStringRef)jlong_to_ptr(juser);546CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);547CFDictionaryRef node = NULL;548jboolean neededNewNode = false;549550if (!path || !name) goto badparams;551552node = copyNodeIfPresent(path, name, user, host);553554if (node) {555neededNewNode = false;556CFRelease(node);557} else {558CFStringRef topKey = NULL;559CFMutableDictionaryRef topValue = NULL;560561neededNewNode = true;562563// copyMutableNode creates the node if necessary564node = copyMutableNode(path, name, user, host, &topKey, &topValue);565throwIfNull(node, "copyMutableNode failed");566567CFPreferencesSetValue(topKey, topValue, name, user, host);568569CFRelease(node);570if (topKey) CFRelease(topKey);571if (topValue) CFRelease(topValue);572}573574badnode:575badparams:576if (path) CFRelease(path);577if (name) CFRelease(name);578579return neededNewNode;580}581582583JNIEXPORT void JNICALL584Java_java_util_prefs_MacOSXPreferencesFile_removeNode585(JNIEnv *env, jobject klass, jobject jpath,586jobject jname, jlong juser, jlong jhost)587{588CFStringRef path = NULL;589CFStringRef name = NULL;590591path = toCF(env, jpath);592if (path != NULL) {593name = toCF(env, jname);594}595CFStringRef user = (CFStringRef)jlong_to_ptr(juser);596CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);597CFStringRef parentName;598CFStringRef childName;599CFDictionaryRef constParent;600601if (!path || !name) goto badparams;602603parentName = copyParentOf(path);604throwIfNull(parentName, "copyParentOf failed");605childName = copyChildOf(path);606throwIfNull(childName, "copyChildOf failed");607608// root node is not allowed to be removed, so parentName is never empty609610constParent = copyNodeIfPresent(parentName, name, user, host);611if (constParent && CFDictionaryContainsKey(constParent, childName)) {612CFStringRef topKey;613CFMutableDictionaryRef topValue;614CFMutableDictionaryRef parent;615616parent = copyMutableNode(parentName, name, user, host,617&topKey, &topValue);618throwIfNull(parent, "copyMutableNode failed");619620CFDictionaryRemoveValue(parent, childName);621CFPreferencesSetValue(topKey, topValue, name, user, host);622623CFRelease(parent);624if (topKey) CFRelease(topKey);625if (topValue) CFRelease(topValue);626} else {627// might be trying to remove the root itself in a non-root file628CFStringRef topKey;629CFDictionaryRef topValue;630copyTreeForPath(path, name, user, host, &topKey, &topValue);631if (topKey) {632if (CFEqual(topKey, path)) {633CFPreferencesSetValue(topKey, NULL, name, user, host);634}635636if (topKey) CFRelease(topKey);637if (topValue) CFRelease(topValue);638}639}640641642badparent:643if (constParent) CFRelease(constParent);644CFRelease(childName);645badchildName:646CFRelease(parentName);647badparentName:648badparams:649if (path) CFRelease(path);650if (name) CFRelease(name);651}652653654// child must end with '/'655JNIEXPORT Boolean JNICALL656Java_java_util_prefs_MacOSXPreferencesFile_addChildToNode657(JNIEnv *env, jobject klass, jobject jpath, jobject jchild,658jobject jname, jlong juser, jlong jhost)659{660// like addNode, but can put a three-level-deep dict into the root file661CFStringRef path = NULL;662CFStringRef child = NULL;663CFStringRef name = NULL;664665path = toCF(env, jpath);666if (path != NULL) {667child = toCF(env, jchild);668}669if (child != NULL) {670name = toCF(env, jname);671}672CFStringRef user = (CFStringRef)jlong_to_ptr(juser);673CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);674CFMutableDictionaryRef parent;675CFDictionaryRef node;676CFStringRef topKey;677CFMutableDictionaryRef topValue;678Boolean beforeAdd = false;679680if (!path || !child || !name) goto badparams;681682node = createEmptyNode();683throwIfNull(node, "createEmptyNode failed");684685// copyMutableNode creates the node if necessary686parent = copyMutableNode(path, name, user, host, &topKey, &topValue);687throwIfNull(parent, "copyMutableNode failed");688beforeAdd = CFDictionaryContainsKey(parent, child);689CFDictionaryAddValue(parent, child, node);690if (!beforeAdd)691beforeAdd = CFDictionaryContainsKey(parent, child);692else693beforeAdd = false;694CFPreferencesSetValue(topKey, topValue, name, user, host);695696CFRelease(parent);697if (topKey) CFRelease(topKey);698if (topValue) CFRelease(topValue);699badparent:700CFRelease(node);701badnode:702badparams:703if (path) CFRelease(path);704if (child) CFRelease(child);705if (name) CFRelease(name);706return beforeAdd;707}708709710JNIEXPORT void JNICALL711Java_java_util_prefs_MacOSXPreferencesFile_removeChildFromNode712(JNIEnv *env, jobject klass, jobject jpath, jobject jchild,713jobject jname, jlong juser, jlong jhost)714{715CFStringRef path = NULL;716CFStringRef child = NULL;717CFStringRef name = NULL;718719path = toCF(env, jpath);720if (path != NULL) {721child = toCF(env, jchild);722}723if (child != NULL) {724name = toCF(env, jname);725}726CFStringRef user = (CFStringRef)jlong_to_ptr(juser);727CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);728CFDictionaryRef constParent;729730if (!path || !child || !name) goto badparams;731732constParent = copyNodeIfPresent(path, name, user, host);733if (constParent && CFDictionaryContainsKey(constParent, child)) {734CFStringRef topKey;735CFMutableDictionaryRef topValue;736CFMutableDictionaryRef parent;737738parent = copyMutableNode(path, name, user, host, &topKey, &topValue);739throwIfNull(parent, "copyMutableNode failed");740741CFDictionaryRemoveValue(parent, child);742CFPreferencesSetValue(topKey, topValue, name, user, host);743744CFRelease(parent);745if (topKey) CFRelease(topKey);746if (topValue) CFRelease(topValue);747}748749badparent:750if (constParent) CFRelease(constParent);751badparams:752if (path) CFRelease(path);753if (child) CFRelease(child);754if (name) CFRelease(name);755}756757758759JNIEXPORT void JNICALL760Java_java_util_prefs_MacOSXPreferencesFile_addKeyToNode761(JNIEnv *env, jobject klass, jobject jpath, jobject jkey, jobject jvalue,762jobject jname, jlong juser, jlong jhost)763{764CFStringRef path = NULL;765CFStringRef key = NULL;766CFStringRef value = NULL;767CFStringRef name = NULL;768769path = toCF(env, jpath);770if (path != NULL) {771key = toCF(env, jkey);772}773if (key != NULL) {774value = toCF(env, jvalue);775}776if (value != NULL) {777name = toCF(env, jname);778}779CFStringRef user = (CFStringRef)jlong_to_ptr(juser);780CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);781CFMutableDictionaryRef node = NULL;782CFStringRef topKey;783CFMutableDictionaryRef topValue;784785if (!path || !key || !value || !name) goto badparams;786787// fixme optimization: check whether old value and new value are identical788node = copyMutableNode(path, name, user, host, &topKey, &topValue);789throwIfNull(node, "copyMutableNode failed");790791CFDictionarySetValue(node, key, value);792CFPreferencesSetValue(topKey, topValue, name, user, host);793794CFRelease(node);795if (topKey) CFRelease(topKey);796if (topValue) CFRelease(topValue);797798badnode:799badparams:800if (path) CFRelease(path);801if (key) CFRelease(key);802if (value) CFRelease(value);803if (name) CFRelease(name);804}805806807JNIEXPORT void JNICALL808Java_java_util_prefs_MacOSXPreferencesFile_removeKeyFromNode809(JNIEnv *env, jobject klass, jobject jpath, jobject jkey,810jobject jname, jlong juser, jlong jhost)811{812CFStringRef path = NULL;813CFStringRef key = NULL;814CFStringRef name = NULL;815816path = toCF(env, jpath);817if (path != NULL) {818key = toCF(env, jkey);819}820if (key != NULL) {821name = toCF(env, jname);822}823CFStringRef user = (CFStringRef)jlong_to_ptr(juser);824CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);825CFDictionaryRef constNode;826827if (!path || !key || !name) goto badparams;828829constNode = copyNodeIfPresent(path, name, user, host);830if (constNode && CFDictionaryContainsKey(constNode, key)) {831CFStringRef topKey;832CFMutableDictionaryRef topValue;833CFMutableDictionaryRef node;834835node = copyMutableNode(path, name, user, host, &topKey, &topValue);836throwIfNull(node, "copyMutableNode failed");837838CFDictionaryRemoveValue(node, key);839CFPreferencesSetValue(topKey, topValue, name, user, host);840841CFRelease(node);842if (topKey) CFRelease(topKey);843if (topValue) CFRelease(topValue);844}845846badnode:847if (constNode) CFRelease(constNode);848badparams:849if (path) CFRelease(path);850if (key) CFRelease(key);851if (name) CFRelease(name);852}853854855// path must end in '/'856JNIEXPORT jstring JNICALL857Java_java_util_prefs_MacOSXPreferencesFile_getKeyFromNode858(JNIEnv *env, jobject klass, jobject jpath, jobject jkey,859jobject jname, jlong juser, jlong jhost)860{861CFStringRef path = NULL;862CFStringRef key = NULL;863CFStringRef name = NULL;864865path = toCF(env, jpath);866if (path != NULL) {867key = toCF(env, jkey);868}869if (key != NULL) {870name = toCF(env, jname);871}872CFStringRef user = (CFStringRef)jlong_to_ptr(juser);873CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);874CFPropertyListRef value;875CFDictionaryRef node;876jstring result = NULL;877878if (!path || !key || !name) goto badparams;879880node = copyNodeIfPresent(path, name, user, host);881if (node) {882value = (CFPropertyListRef)CFDictionaryGetValue(node, key);883if (!value) {884// key doesn't exist, or other error - no Java errors available885result = NULL;886} else {887CFStringRef cfString = copyToCFString(env, value);888if ((*env)->ExceptionOccurred(env)) {889// memory error in copyToCFString890result = NULL;891} else if (cfString == NULL) {892// bogus value type in prefs file - no Java errors available893result = NULL;894} else {895// good cfString896result = toJavaString(env, cfString);897CFRelease(cfString);898}899}900CFRelease(node);901}902903badparams:904if (path) CFRelease(path);905if (key) CFRelease(key);906if (name) CFRelease(name);907908return result;909}910911912typedef struct {913jarray result;914JNIEnv *env;915CFIndex used;916Boolean allowSlash;917} BuildJavaArrayArgs;918919// CFDictionary applier function that builds an array of Java strings920// from a CFDictionary of CFPropertyListRefs.921// If args->allowSlash, only strings that end in '/' are added to the array,922// with the slash removed. Otherwise, only strings that do not end in '/'923// are added.924// args->result must already exist and be large enough to hold all925// strings from the dictionary.926// After complete application, args->result may not be full because927// some of the dictionary values weren't convertible to string. In928// this case, args->used will be the count of used elements.929static void BuildJavaArrayFn(const void *key, const void *value, void *context)930{931BuildJavaArrayArgs *args = (BuildJavaArrayArgs *)context;932CFPropertyListRef propkey = (CFPropertyListRef)key;933CFStringRef cfString = NULL;934JNIEnv *env = args->env;935936if ((*env)->ExceptionOccurred(env)) return; // already failed937938cfString = copyToCFString(env, propkey);939if ((*env)->ExceptionOccurred(env)) {940// memory error in copyToCFString941} else if (!cfString) {942// bogus value type in prefs file - no Java errors available943} else if (args->allowSlash != CFStringHasSuffix(cfString, CFSTR("/"))) {944// wrong suffix - ignore945} else {946// good cfString947jstring javaString;948if (args->allowSlash) {949CFRange range = CFRangeMake(0, CFStringGetLength(cfString) - 1);950CFStringRef s = CFStringCreateWithSubstring(NULL, cfString, range);951CFRelease(cfString);952cfString = s;953}954if (CFStringGetLength(cfString) <= 0) goto bad; // ignore empty955javaString = toJavaString(env, cfString);956if ((*env)->ExceptionOccurred(env)) goto bad;957(*env)->SetObjectArrayElement(env, args->result,args->used,javaString);958if ((*env)->ExceptionOccurred(env)) goto bad;959args->used++;960}961962bad:963if (cfString) CFRelease(cfString);964}965966967static jarray getStringsForNode(JNIEnv *env, jobject klass, jobject jpath,968jobject jname, jlong juser, jlong jhost,969Boolean allowSlash)970{971CFStringRef path = NULL;972CFStringRef name = NULL;973974path = toCF(env, jpath);975if (path != NULL) {976name = toCF(env, jname);977}978CFStringRef user = (CFStringRef)jlong_to_ptr(juser);979CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);980CFDictionaryRef node;981jarray result = NULL;982CFIndex count;983984if (!path || !name) goto badparams;985986node = copyNodeIfPresent(path, name, user, host);987if (!node) {988result = createJavaStringArray(env, 0);989} else {990count = CFDictionaryGetCount(node);991result = createJavaStringArray(env, count);992if (result) {993BuildJavaArrayArgs args;994args.result = result;995args.env = env;996args.used = 0;997args.allowSlash = allowSlash;998CFDictionaryApplyFunction(node, BuildJavaArrayFn, &args);999if (!(*env)->ExceptionOccurred(env)) {1000// array construction succeeded1001if (args.used < count) {1002// finished array is smaller than expected.1003// Make a new array of precisely the right size.1004jarray newresult = createJavaStringArray(env, args.used);1005if (newresult) {1006JVM_ArrayCopy(env,0, result,0, newresult,0, args.used);1007result = newresult;1008}1009}1010}1011}10121013CFRelease(node);1014}10151016badparams:1017if (path) CFRelease(path);1018if (name) CFRelease(name);10191020return result;1021}102210231024JNIEXPORT jarray JNICALL1025Java_java_util_prefs_MacOSXPreferencesFile_getKeysForNode1026(JNIEnv *env, jobject klass, jobject jpath,1027jobject jname, jlong juser, jlong jhost)1028{1029return getStringsForNode(env, klass, jpath, jname, juser, jhost, false);1030}10311032JNIEXPORT jarray JNICALL1033Java_java_util_prefs_MacOSXPreferencesFile_getChildrenForNode1034(JNIEnv *env, jobject klass, jobject jpath,1035jobject jname, jlong juser, jlong jhost)1036{1037return getStringsForNode(env, klass, jpath, jname, juser, jhost, true);1038}103910401041// Returns false on error instead of throwing.1042JNIEXPORT jboolean JNICALL1043Java_java_util_prefs_MacOSXPreferencesFile_synchronize1044(JNIEnv *env, jobject klass,1045jstring jname, jlong juser, jlong jhost)1046{1047CFStringRef name = toCF(env, jname);1048CFStringRef user = (CFStringRef)jlong_to_ptr(juser);1049CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);1050jboolean result = 0;10511052if (name) {1053result = CFPreferencesSynchronize(name, user, host);1054CFRelease(name);1055}10561057return result;1058}105910601061