Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.prefs/macosx/native/libprefs/MacOSXPreferencesFile.m
41149 views
1
/*
2
* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
/*
27
Hierarchical storage layout:
28
29
<dict>
30
<key>/</key>
31
<dict>
32
<key>foo</key>
33
<string>/foo's value</string>
34
<key>foo/</key>
35
<dict>
36
<key>bar</key>
37
<string>/foo/bar's value</string>
38
</dict>
39
</dict>
40
</dict>
41
42
Java pref nodes are stored in several different files. Pref nodes
43
with at least three components in the node name (e.g. /com/MyCompany/MyApp/)
44
are stored in a CF prefs file with the first three components as the name.
45
This way, all preferences for MyApp end up in com.MyCompany.MyApp.plist .
46
Pref nodes with shorter names are stored in com.apple.java.util.prefs.plist
47
48
The filesystem is assumed to be case-insensitive (like HFS+).
49
Java pref node names are case-sensitive. If two pref node names differ
50
only in case, they may end up in the same pref file. This is ok
51
because the CF keys identifying the node span the entire absolute path
52
to the node and are case-sensitive.
53
54
Java node names may contain '.' . When mapping to the CF file name,
55
these dots are left as-is, even though '/' is mapped to '.' .
56
This is ok because the CF key contains the correct node name.
57
*/
58
59
60
61
#include <CoreFoundation/CoreFoundation.h>
62
63
#include "jni_util.h"
64
#include "jlong.h"
65
#include "jvm.h"
66
#include "java_util_prefs_MacOSXPreferencesFile.h"
67
68
/*
69
* Declare library specific JNI_Onload entry if static build
70
*/
71
DEF_STATIC_JNI_OnLoad
72
73
74
// Throw an OutOfMemoryError with the given message.
75
static void throwOutOfMemoryError(JNIEnv *env, const char *msg)
76
{
77
static jclass exceptionClass = NULL;
78
jclass c;
79
80
(*env)->ExceptionClear(env); // If an exception is pending, clear it before
81
// calling FindClass() and/or ThrowNew().
82
if (exceptionClass) {
83
c = exceptionClass;
84
} else {
85
c = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
86
if ((*env)->ExceptionOccurred(env)) return;
87
exceptionClass = (*env)->NewGlobalRef(env, c);
88
}
89
90
(*env)->ThrowNew(env, c, msg);
91
}
92
93
94
// throwIfNull macro
95
// If var is NULL, throw an OutOfMemoryError and goto badvar.
96
// var must be a variable. env must be the current JNIEnv.
97
// fixme throw BackingStoreExceptions sometimes?
98
#define throwIfNull(var, msg) \
99
do { \
100
if (var == NULL) { \
101
throwOutOfMemoryError(env, msg); \
102
goto bad##var; \
103
} \
104
} while (0)
105
106
107
// Converts CFNumber, CFBoolean, CFString to CFString
108
// returns NULL if value is of some other type
109
// throws and returns NULL on memory error
110
// result must be released (even if value was already a CFStringRef)
111
// value must not be null
112
static CFStringRef copyToCFString(JNIEnv *env, CFTypeRef value)
113
{
114
CFStringRef result;
115
CFTypeID type;
116
117
type = CFGetTypeID(value);
118
119
if (type == CFStringGetTypeID()) {
120
result = (CFStringRef)CFRetain(value);
121
}
122
else if (type == CFBooleanGetTypeID()) {
123
// Java Preferences API expects "true" and "false" for boolean values.
124
result = CFStringCreateCopy(NULL, (value == kCFBooleanTrue) ? CFSTR("true") : CFSTR("false"));
125
throwIfNull(result, "copyToCFString failed");
126
}
127
else if (type == CFNumberGetTypeID()) {
128
CFNumberRef number = (CFNumberRef) value;
129
if (CFNumberIsFloatType(number)) {
130
double d;
131
CFNumberGetValue(number, kCFNumberDoubleType, &d);
132
result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%g"), d);
133
throwIfNull(result, "copyToCFString failed");
134
}
135
else {
136
long l;
137
CFNumberGetValue(number, kCFNumberLongType, &l);
138
result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%ld"), l);
139
throwIfNull(result, "copyToCFString failed");
140
}
141
}
142
else {
143
// unknown type - return NULL
144
result = NULL;
145
}
146
147
badresult:
148
return result;
149
}
150
151
152
// Create a Java string from the given CF string.
153
// returns NULL if cfString is NULL
154
// throws and returns NULL on memory error
155
static jstring toJavaString(JNIEnv *env, CFStringRef cfString)
156
{
157
if (cfString == NULL) {
158
return NULL;
159
} else {
160
jstring javaString = NULL;
161
162
CFIndex length = CFStringGetLength(cfString);
163
const UniChar *constchars = CFStringGetCharactersPtr(cfString);
164
if (constchars) {
165
javaString = (*env)->NewString(env, constchars, length);
166
} else {
167
UniChar *chars = malloc(length * sizeof(UniChar));
168
throwIfNull(chars, "toJavaString failed");
169
CFStringGetCharacters(cfString, CFRangeMake(0, length), chars);
170
javaString = (*env)->NewString(env, chars, length);
171
free(chars);
172
}
173
badchars:
174
return javaString;
175
}
176
}
177
178
179
180
// Create a CF string from the given Java string.
181
// returns NULL if javaString is NULL
182
// throws and returns NULL on memory error
183
static CFStringRef toCF(JNIEnv *env, jstring javaString)
184
{
185
if (javaString == NULL) {
186
return NULL;
187
} else {
188
CFStringRef result = NULL;
189
jsize length = (*env)->GetStringLength(env, javaString);
190
const jchar *chars = (*env)->GetStringChars(env, javaString, NULL);
191
throwIfNull(chars, "toCF failed");
192
result =
193
CFStringCreateWithCharacters(NULL, (const UniChar *)chars, length);
194
(*env)->ReleaseStringChars(env, javaString, chars);
195
throwIfNull(result, "toCF failed");
196
badchars:
197
badresult:
198
return result;
199
}
200
}
201
202
203
// Create an empty Java string array of the given size.
204
// Throws and returns NULL on error.
205
static jarray createJavaStringArray(JNIEnv *env, CFIndex count)
206
{
207
static jclass stringClass = NULL;
208
jclass c;
209
210
if (stringClass) {
211
c = stringClass;
212
} else {
213
c = (*env)->FindClass(env, "java/lang/String");
214
if ((*env)->ExceptionOccurred(env)) return NULL;
215
stringClass = (*env)->NewGlobalRef(env, c);
216
}
217
218
return (*env)->NewObjectArray(env, count, c, NULL); // AWT_THREADING Safe (known object)
219
}
220
221
222
// Java accessors for CF constants.
223
JNIEXPORT jlong JNICALL
224
Java_java_util_prefs_MacOSXPreferencesFile_currentUser(JNIEnv *env,
225
jobject klass)
226
{
227
return ptr_to_jlong(kCFPreferencesCurrentUser);
228
}
229
230
JNIEXPORT jlong JNICALL
231
Java_java_util_prefs_MacOSXPreferencesFile_anyUser(JNIEnv *env, jobject klass)
232
{
233
return ptr_to_jlong(kCFPreferencesAnyUser);
234
}
235
236
JNIEXPORT jlong JNICALL
237
Java_java_util_prefs_MacOSXPreferencesFile_currentHost(JNIEnv *env,
238
jobject klass)
239
{
240
return ptr_to_jlong(kCFPreferencesCurrentHost);
241
}
242
243
JNIEXPORT jlong JNICALL
244
Java_java_util_prefs_MacOSXPreferencesFile_anyHost(JNIEnv *env, jobject klass)
245
{
246
return ptr_to_jlong(kCFPreferencesAnyHost);
247
}
248
249
250
// Create an empty node.
251
// Does not store the node in any prefs file.
252
// returns NULL on memory error
253
static CFMutableDictionaryRef createEmptyNode(void)
254
{
255
return CFDictionaryCreateMutable(NULL, 0,
256
&kCFTypeDictionaryKeyCallBacks,
257
&kCFTypeDictionaryValueCallBacks);
258
}
259
260
261
// Create a string that consists of path minus its last component.
262
// path must end with '/'
263
// The result will end in '/' (unless path itself is '/')
264
static CFStringRef copyParentOf(CFStringRef path)
265
{
266
CFRange searchRange;
267
CFRange slashRange;
268
CFRange parentRange;
269
Boolean found;
270
271
searchRange = CFRangeMake(0, CFStringGetLength(path) - 1);
272
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange,
273
kCFCompareBackwards, &slashRange);
274
if (!found) return CFSTR("");
275
parentRange = CFRangeMake(0, slashRange.location + 1); // include '/'
276
return CFStringCreateWithSubstring(NULL, path, parentRange);
277
}
278
279
280
// Create a string that consists of path's last component.
281
// path must end with '/'
282
// The result will end in '/'.
283
// The result will not start with '/' (unless path itself is '/')
284
static CFStringRef copyChildOf(CFStringRef path)
285
{
286
CFRange searchRange;
287
CFRange slashRange;
288
CFRange childRange;
289
Boolean found;
290
CFIndex length = CFStringGetLength(path);
291
292
searchRange = CFRangeMake(0, length - 1);
293
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange,
294
kCFCompareBackwards, &slashRange);
295
if (!found) return CFSTR("");
296
childRange = CFRangeMake(slashRange.location + 1,
297
length - slashRange.location - 1); // skip '/'
298
return CFStringCreateWithSubstring(NULL, path, childRange);
299
}
300
301
302
// Return the first three components of path, with leading and trailing '/'.
303
// If path does not have three components, return NULL.
304
// path must begin and end in '/'
305
static CFStringRef copyFirstThreeComponentsOf(CFStringRef path)
306
{
307
CFRange searchRange;
308
CFRange slashRange;
309
CFRange prefixRange;
310
CFStringRef prefix;
311
Boolean found;
312
CFIndex length = CFStringGetLength(path);
313
314
searchRange = CFRangeMake(1, length - 1); // skip leading '/'
315
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,
316
&slashRange);
317
if (!found) return NULL; // no second slash!
318
319
searchRange = CFRangeMake(slashRange.location + 1,
320
length - slashRange.location - 1);
321
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,
322
&slashRange);
323
if (!found) return NULL; // no third slash!
324
325
searchRange = CFRangeMake(slashRange.location + 1,
326
length - slashRange.location - 1);
327
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,
328
&slashRange);
329
if (!found) return NULL; // no fourth slash!
330
331
prefixRange = CFRangeMake(0, slashRange.location + 1); // keep last '/'
332
prefix = CFStringCreateWithSubstring(NULL, path, prefixRange);
333
334
return prefix;
335
}
336
337
338
// Copy the CFPreferences key and value at the base of path's tree.
339
// path must end in '/'
340
// topKey or topValue may be NULL
341
// Returns NULL on error or if there is no tree for path in this file.
342
static void copyTreeForPath(CFStringRef path, CFStringRef name,
343
CFStringRef user, CFStringRef host,
344
CFStringRef *topKey, CFDictionaryRef *topValue)
345
{
346
CFStringRef key;
347
CFPropertyListRef value;
348
349
if (topKey) *topKey = NULL;
350
if (topValue) *topValue = NULL;
351
352
if (CFEqual(name, CFSTR("com.apple.java.util.prefs"))) {
353
// Top-level file. Only key "/" is an acceptable root.
354
key = (CFStringRef) CFRetain(CFSTR("/"));
355
} else {
356
// Second-level file. Key must be the first three components of path.
357
key = copyFirstThreeComponentsOf(path);
358
if (!key) return;
359
}
360
361
value = CFPreferencesCopyValue(key, name, user, host);
362
if (value) {
363
if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
364
// (key, value) is acceptable
365
if (topKey) *topKey = (CFStringRef)CFRetain(key);
366
if (topValue) *topValue = (CFDictionaryRef)CFRetain(value);
367
}
368
CFRelease(value);
369
}
370
CFRelease(key);
371
}
372
373
374
// Find the node for path in the given tree.
375
// Returns NULL on error or if path doesn't have a node in this tree.
376
// path must end in '/'
377
static CFDictionaryRef copyNodeInTree(CFStringRef path, CFStringRef topKey,
378
CFDictionaryRef topValue)
379
{
380
CFMutableStringRef p;
381
CFDictionaryRef result = NULL;
382
383
p = CFStringCreateMutableCopy(NULL, 0, path);
384
if (!p) return NULL;
385
CFStringDelete(p, CFRangeMake(0, CFStringGetLength(topKey)));
386
result = topValue;
387
388
while (CFStringGetLength(p) > 0) {
389
CFDictionaryRef child;
390
CFStringRef part = NULL;
391
CFRange slashRange = CFStringFind(p, CFSTR("/"), 0);
392
// guaranteed to succeed because path must end in '/'
393
CFRange partRange = CFRangeMake(0, slashRange.location + 1);
394
part = CFStringCreateWithSubstring(NULL, p, partRange);
395
if (!part) { result = NULL; break; }
396
CFStringDelete(p, partRange);
397
398
child = CFDictionaryGetValue(result, part);
399
CFRelease(part);
400
if (child && CFGetTypeID(child) == CFDictionaryGetTypeID()) {
401
// continue search
402
result = child;
403
} else {
404
// didn't find target node
405
result = NULL;
406
break;
407
}
408
}
409
410
CFRelease(p);
411
if (result) return (CFDictionaryRef)CFRetain(result);
412
else return NULL;
413
}
414
415
416
// Return a retained copy of the node at path from the given file.
417
// path must end in '/'
418
// returns NULL if node doesn't exist.
419
// returns NULL if the value for key "path" isn't a valid node.
420
static CFDictionaryRef copyNodeIfPresent(CFStringRef path, CFStringRef name,
421
CFStringRef user, CFStringRef host)
422
{
423
CFStringRef topKey;
424
CFDictionaryRef topValue;
425
CFDictionaryRef result;
426
427
copyTreeForPath(path, name, user, host, &topKey, &topValue);
428
if (!topKey) return NULL;
429
430
result = copyNodeInTree(path, topKey, topValue);
431
432
CFRelease(topKey);
433
if (topValue) CFRelease(topValue);
434
return result;
435
}
436
437
438
// Create a new tree that would store path in the given file.
439
// Only the root of the tree is created, not all of the links leading to path.
440
// returns NULL on error
441
static void createTreeForPath(CFStringRef path, CFStringRef name,
442
CFStringRef user, CFStringRef host,
443
CFStringRef *outTopKey,
444
CFMutableDictionaryRef *outTopValue)
445
{
446
*outTopKey = NULL;
447
*outTopValue = NULL;
448
449
// if name is "com.apple.java.util.prefs" then create tree "/"
450
// else create tree "/foo/bar/baz/"
451
// "com.apple.java.util.prefs.plist" is also in MacOSXPreferences.java
452
if (CFEqual(name, CFSTR("com.apple.java.util.prefs"))) {
453
*outTopKey = CFSTR("/");
454
*outTopValue = createEmptyNode();
455
} else {
456
CFStringRef prefix = copyFirstThreeComponentsOf(path);
457
if (prefix) {
458
*outTopKey = prefix;
459
*outTopValue = createEmptyNode();
460
}
461
}
462
}
463
464
465
// Return a mutable copy of the tree containing path and the dict for
466
// path itself. *outTopKey and *outTopValue can be used to write the
467
// modified tree back to the prefs file.
468
// *outTopKey and *outTopValue must be released iff the actual return
469
// value is not NULL.
470
static CFMutableDictionaryRef
471
copyMutableNode(CFStringRef path, CFStringRef name,
472
CFStringRef user, CFStringRef host,
473
CFStringRef *outTopKey,
474
CFMutableDictionaryRef *outTopValue)
475
{
476
CFStringRef topKey = NULL;
477
CFDictionaryRef oldTopValue = NULL;
478
CFMutableDictionaryRef topValue;
479
CFMutableDictionaryRef result = NULL;
480
CFMutableStringRef p;
481
482
if (outTopKey) *outTopKey = NULL;
483
if (outTopValue) *outTopValue = NULL;
484
485
copyTreeForPath(path, name, user, host, &topKey, &oldTopValue);
486
if (!topKey) {
487
createTreeForPath(path, name, user, host, &topKey, &topValue);
488
} else {
489
topValue = (CFMutableDictionaryRef)
490
CFPropertyListCreateDeepCopy(NULL, (CFPropertyListRef)oldTopValue,
491
kCFPropertyListMutableContainers);
492
}
493
if (!topValue) goto badtopValue;
494
495
p = CFStringCreateMutableCopy(NULL, 0, path);
496
if (!p) goto badp;
497
CFStringDelete(p, CFRangeMake(0, CFStringGetLength(topKey)));
498
result = topValue;
499
500
while (CFStringGetLength(p) > 0) {
501
CFMutableDictionaryRef child;
502
CFStringRef part = NULL;
503
CFRange slashRange = CFStringFind(p, CFSTR("/"), 0);
504
// guaranteed to succeed because path must end in '/'
505
CFRange partRange = CFRangeMake(0, slashRange.location + 1);
506
part = CFStringCreateWithSubstring(NULL, p, partRange);
507
if (!part) { result = NULL; break; }
508
CFStringDelete(p, partRange);
509
510
child = (CFMutableDictionaryRef)CFDictionaryGetValue(result, part);
511
if (child && CFGetTypeID(child) == CFDictionaryGetTypeID()) {
512
// continue search
513
result = child;
514
} else {
515
// didn't find target node - add it and continue
516
child = createEmptyNode();
517
if (!child) { CFRelease(part); result = NULL; break; }
518
CFDictionaryAddValue(result, part, child);
519
result = child;
520
}
521
CFRelease(part);
522
}
523
524
if (result) {
525
*outTopKey = (CFStringRef)CFRetain(topKey);
526
*outTopValue = (CFMutableDictionaryRef)CFRetain(topValue);
527
CFRetain(result);
528
}
529
530
CFRelease(p);
531
badp:
532
CFRelease(topValue);
533
badtopValue:
534
if (topKey) CFRelease(topKey);
535
if (oldTopValue) CFRelease(oldTopValue);
536
return result;
537
}
538
539
540
JNIEXPORT jboolean JNICALL
541
Java_java_util_prefs_MacOSXPreferencesFile_addNode
542
(JNIEnv *env, jobject klass, jobject jpath,
543
jobject jname, jlong juser, jlong jhost)
544
{
545
CFStringRef path = NULL;
546
CFStringRef name = NULL;
547
548
path = toCF(env, jpath);
549
if (path != NULL) {
550
name = toCF(env, jname);
551
}
552
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
553
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
554
CFDictionaryRef node = NULL;
555
jboolean neededNewNode = false;
556
557
if (!path || !name) goto badparams;
558
559
node = copyNodeIfPresent(path, name, user, host);
560
561
if (node) {
562
neededNewNode = false;
563
CFRelease(node);
564
} else {
565
CFStringRef topKey = NULL;
566
CFMutableDictionaryRef topValue = NULL;
567
568
neededNewNode = true;
569
570
// copyMutableNode creates the node if necessary
571
node = copyMutableNode(path, name, user, host, &topKey, &topValue);
572
throwIfNull(node, "copyMutableNode failed");
573
574
CFPreferencesSetValue(topKey, topValue, name, user, host);
575
576
CFRelease(node);
577
if (topKey) CFRelease(topKey);
578
if (topValue) CFRelease(topValue);
579
}
580
581
badnode:
582
badparams:
583
if (path) CFRelease(path);
584
if (name) CFRelease(name);
585
586
return neededNewNode;
587
}
588
589
590
JNIEXPORT void JNICALL
591
Java_java_util_prefs_MacOSXPreferencesFile_removeNode
592
(JNIEnv *env, jobject klass, jobject jpath,
593
jobject jname, jlong juser, jlong jhost)
594
{
595
CFStringRef path = NULL;
596
CFStringRef name = NULL;
597
598
path = toCF(env, jpath);
599
if (path != NULL) {
600
name = toCF(env, jname);
601
}
602
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
603
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
604
CFStringRef parentName;
605
CFStringRef childName;
606
CFDictionaryRef constParent;
607
608
if (!path || !name) goto badparams;
609
610
parentName = copyParentOf(path);
611
throwIfNull(parentName, "copyParentOf failed");
612
childName = copyChildOf(path);
613
throwIfNull(childName, "copyChildOf failed");
614
615
// root node is not allowed to be removed, so parentName is never empty
616
617
constParent = copyNodeIfPresent(parentName, name, user, host);
618
if (constParent && CFDictionaryContainsKey(constParent, childName)) {
619
CFStringRef topKey;
620
CFMutableDictionaryRef topValue;
621
CFMutableDictionaryRef parent;
622
623
parent = copyMutableNode(parentName, name, user, host,
624
&topKey, &topValue);
625
throwIfNull(parent, "copyMutableNode failed");
626
627
CFDictionaryRemoveValue(parent, childName);
628
CFPreferencesSetValue(topKey, topValue, name, user, host);
629
630
CFRelease(parent);
631
if (topKey) CFRelease(topKey);
632
if (topValue) CFRelease(topValue);
633
} else {
634
// might be trying to remove the root itself in a non-root file
635
CFStringRef topKey;
636
CFDictionaryRef topValue;
637
copyTreeForPath(path, name, user, host, &topKey, &topValue);
638
if (topKey) {
639
if (CFEqual(topKey, path)) {
640
CFPreferencesSetValue(topKey, NULL, name, user, host);
641
}
642
643
if (topKey) CFRelease(topKey);
644
if (topValue) CFRelease(topValue);
645
}
646
}
647
648
649
badparent:
650
if (constParent) CFRelease(constParent);
651
CFRelease(childName);
652
badchildName:
653
CFRelease(parentName);
654
badparentName:
655
badparams:
656
if (path) CFRelease(path);
657
if (name) CFRelease(name);
658
}
659
660
661
// child must end with '/'
662
JNIEXPORT Boolean JNICALL
663
Java_java_util_prefs_MacOSXPreferencesFile_addChildToNode
664
(JNIEnv *env, jobject klass, jobject jpath, jobject jchild,
665
jobject jname, jlong juser, jlong jhost)
666
{
667
// like addNode, but can put a three-level-deep dict into the root file
668
CFStringRef path = NULL;
669
CFStringRef child = NULL;
670
CFStringRef name = NULL;
671
672
path = toCF(env, jpath);
673
if (path != NULL) {
674
child = toCF(env, jchild);
675
}
676
if (child != NULL) {
677
name = toCF(env, jname);
678
}
679
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
680
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
681
CFMutableDictionaryRef parent;
682
CFDictionaryRef node;
683
CFStringRef topKey;
684
CFMutableDictionaryRef topValue;
685
Boolean beforeAdd = false;
686
687
if (!path || !child || !name) goto badparams;
688
689
node = createEmptyNode();
690
throwIfNull(node, "createEmptyNode failed");
691
692
// copyMutableNode creates the node if necessary
693
parent = copyMutableNode(path, name, user, host, &topKey, &topValue);
694
throwIfNull(parent, "copyMutableNode failed");
695
beforeAdd = CFDictionaryContainsKey(parent, child);
696
CFDictionaryAddValue(parent, child, node);
697
if (!beforeAdd)
698
beforeAdd = CFDictionaryContainsKey(parent, child);
699
else
700
beforeAdd = false;
701
CFPreferencesSetValue(topKey, topValue, name, user, host);
702
703
CFRelease(parent);
704
if (topKey) CFRelease(topKey);
705
if (topValue) CFRelease(topValue);
706
badparent:
707
CFRelease(node);
708
badnode:
709
badparams:
710
if (path) CFRelease(path);
711
if (child) CFRelease(child);
712
if (name) CFRelease(name);
713
return beforeAdd;
714
}
715
716
717
JNIEXPORT void JNICALL
718
Java_java_util_prefs_MacOSXPreferencesFile_removeChildFromNode
719
(JNIEnv *env, jobject klass, jobject jpath, jobject jchild,
720
jobject jname, jlong juser, jlong jhost)
721
{
722
CFStringRef path = NULL;
723
CFStringRef child = NULL;
724
CFStringRef name = NULL;
725
726
path = toCF(env, jpath);
727
if (path != NULL) {
728
child = toCF(env, jchild);
729
}
730
if (child != NULL) {
731
name = toCF(env, jname);
732
}
733
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
734
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
735
CFDictionaryRef constParent;
736
737
if (!path || !child || !name) goto badparams;
738
739
constParent = copyNodeIfPresent(path, name, user, host);
740
if (constParent && CFDictionaryContainsKey(constParent, child)) {
741
CFStringRef topKey;
742
CFMutableDictionaryRef topValue;
743
CFMutableDictionaryRef parent;
744
745
parent = copyMutableNode(path, name, user, host, &topKey, &topValue);
746
throwIfNull(parent, "copyMutableNode failed");
747
748
CFDictionaryRemoveValue(parent, child);
749
CFPreferencesSetValue(topKey, topValue, name, user, host);
750
751
CFRelease(parent);
752
if (topKey) CFRelease(topKey);
753
if (topValue) CFRelease(topValue);
754
}
755
756
badparent:
757
if (constParent) CFRelease(constParent);
758
badparams:
759
if (path) CFRelease(path);
760
if (child) CFRelease(child);
761
if (name) CFRelease(name);
762
}
763
764
765
766
JNIEXPORT void JNICALL
767
Java_java_util_prefs_MacOSXPreferencesFile_addKeyToNode
768
(JNIEnv *env, jobject klass, jobject jpath, jobject jkey, jobject jvalue,
769
jobject jname, jlong juser, jlong jhost)
770
{
771
CFStringRef path = NULL;
772
CFStringRef key = NULL;
773
CFStringRef value = NULL;
774
CFStringRef name = NULL;
775
776
path = toCF(env, jpath);
777
if (path != NULL) {
778
key = toCF(env, jkey);
779
}
780
if (key != NULL) {
781
value = toCF(env, jvalue);
782
}
783
if (value != NULL) {
784
name = toCF(env, jname);
785
}
786
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
787
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
788
CFMutableDictionaryRef node = NULL;
789
CFStringRef topKey;
790
CFMutableDictionaryRef topValue;
791
792
if (!path || !key || !value || !name) goto badparams;
793
794
// fixme optimization: check whether old value and new value are identical
795
node = copyMutableNode(path, name, user, host, &topKey, &topValue);
796
throwIfNull(node, "copyMutableNode failed");
797
798
CFDictionarySetValue(node, key, value);
799
CFPreferencesSetValue(topKey, topValue, name, user, host);
800
801
CFRelease(node);
802
if (topKey) CFRelease(topKey);
803
if (topValue) CFRelease(topValue);
804
805
badnode:
806
badparams:
807
if (path) CFRelease(path);
808
if (key) CFRelease(key);
809
if (value) CFRelease(value);
810
if (name) CFRelease(name);
811
}
812
813
814
JNIEXPORT void JNICALL
815
Java_java_util_prefs_MacOSXPreferencesFile_removeKeyFromNode
816
(JNIEnv *env, jobject klass, jobject jpath, jobject jkey,
817
jobject jname, jlong juser, jlong jhost)
818
{
819
CFStringRef path = NULL;
820
CFStringRef key = NULL;
821
CFStringRef name = NULL;
822
823
path = toCF(env, jpath);
824
if (path != NULL) {
825
key = toCF(env, jkey);
826
}
827
if (key != NULL) {
828
name = toCF(env, jname);
829
}
830
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
831
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
832
CFDictionaryRef constNode;
833
834
if (!path || !key || !name) goto badparams;
835
836
constNode = copyNodeIfPresent(path, name, user, host);
837
if (constNode && CFDictionaryContainsKey(constNode, key)) {
838
CFStringRef topKey;
839
CFMutableDictionaryRef topValue;
840
CFMutableDictionaryRef node;
841
842
node = copyMutableNode(path, name, user, host, &topKey, &topValue);
843
throwIfNull(node, "copyMutableNode failed");
844
845
CFDictionaryRemoveValue(node, key);
846
CFPreferencesSetValue(topKey, topValue, name, user, host);
847
848
CFRelease(node);
849
if (topKey) CFRelease(topKey);
850
if (topValue) CFRelease(topValue);
851
}
852
853
badnode:
854
if (constNode) CFRelease(constNode);
855
badparams:
856
if (path) CFRelease(path);
857
if (key) CFRelease(key);
858
if (name) CFRelease(name);
859
}
860
861
862
// path must end in '/'
863
JNIEXPORT jstring JNICALL
864
Java_java_util_prefs_MacOSXPreferencesFile_getKeyFromNode
865
(JNIEnv *env, jobject klass, jobject jpath, jobject jkey,
866
jobject jname, jlong juser, jlong jhost)
867
{
868
CFStringRef path = NULL;
869
CFStringRef key = NULL;
870
CFStringRef name = NULL;
871
872
path = toCF(env, jpath);
873
if (path != NULL) {
874
key = toCF(env, jkey);
875
}
876
if (key != NULL) {
877
name = toCF(env, jname);
878
}
879
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
880
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
881
CFPropertyListRef value;
882
CFDictionaryRef node;
883
jstring result = NULL;
884
885
if (!path || !key || !name) goto badparams;
886
887
node = copyNodeIfPresent(path, name, user, host);
888
if (node) {
889
value = (CFPropertyListRef)CFDictionaryGetValue(node, key);
890
if (!value) {
891
// key doesn't exist, or other error - no Java errors available
892
result = NULL;
893
} else {
894
CFStringRef cfString = copyToCFString(env, value);
895
if ((*env)->ExceptionOccurred(env)) {
896
// memory error in copyToCFString
897
result = NULL;
898
} else if (cfString == NULL) {
899
// bogus value type in prefs file - no Java errors available
900
result = NULL;
901
} else {
902
// good cfString
903
result = toJavaString(env, cfString);
904
CFRelease(cfString);
905
}
906
}
907
CFRelease(node);
908
}
909
910
badparams:
911
if (path) CFRelease(path);
912
if (key) CFRelease(key);
913
if (name) CFRelease(name);
914
915
return result;
916
}
917
918
919
typedef struct {
920
jarray result;
921
JNIEnv *env;
922
CFIndex used;
923
Boolean allowSlash;
924
} BuildJavaArrayArgs;
925
926
// CFDictionary applier function that builds an array of Java strings
927
// from a CFDictionary of CFPropertyListRefs.
928
// If args->allowSlash, only strings that end in '/' are added to the array,
929
// with the slash removed. Otherwise, only strings that do not end in '/'
930
// are added.
931
// args->result must already exist and be large enough to hold all
932
// strings from the dictionary.
933
// After complete application, args->result may not be full because
934
// some of the dictionary values weren't convertible to string. In
935
// this case, args->used will be the count of used elements.
936
static void BuildJavaArrayFn(const void *key, const void *value, void *context)
937
{
938
BuildJavaArrayArgs *args = (BuildJavaArrayArgs *)context;
939
CFPropertyListRef propkey = (CFPropertyListRef)key;
940
CFStringRef cfString = NULL;
941
JNIEnv *env = args->env;
942
943
if ((*env)->ExceptionOccurred(env)) return; // already failed
944
945
cfString = copyToCFString(env, propkey);
946
if ((*env)->ExceptionOccurred(env)) {
947
// memory error in copyToCFString
948
} else if (!cfString) {
949
// bogus value type in prefs file - no Java errors available
950
} else if (args->allowSlash != CFStringHasSuffix(cfString, CFSTR("/"))) {
951
// wrong suffix - ignore
952
} else {
953
// good cfString
954
jstring javaString;
955
if (args->allowSlash) {
956
CFRange range = CFRangeMake(0, CFStringGetLength(cfString) - 1);
957
CFStringRef s = CFStringCreateWithSubstring(NULL, cfString, range);
958
CFRelease(cfString);
959
cfString = s;
960
}
961
if (CFStringGetLength(cfString) <= 0) goto bad; // ignore empty
962
javaString = toJavaString(env, cfString);
963
if ((*env)->ExceptionOccurred(env)) goto bad;
964
(*env)->SetObjectArrayElement(env, args->result,args->used,javaString);
965
if ((*env)->ExceptionOccurred(env)) goto bad;
966
args->used++;
967
}
968
969
bad:
970
if (cfString) CFRelease(cfString);
971
}
972
973
974
static jarray getStringsForNode(JNIEnv *env, jobject klass, jobject jpath,
975
jobject jname, jlong juser, jlong jhost,
976
Boolean allowSlash)
977
{
978
CFStringRef path = NULL;
979
CFStringRef name = NULL;
980
981
path = toCF(env, jpath);
982
if (path != NULL) {
983
name = toCF(env, jname);
984
}
985
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
986
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
987
CFDictionaryRef node;
988
jarray result = NULL;
989
CFIndex count;
990
991
if (!path || !name) goto badparams;
992
993
node = copyNodeIfPresent(path, name, user, host);
994
if (!node) {
995
result = createJavaStringArray(env, 0);
996
} else {
997
count = CFDictionaryGetCount(node);
998
result = createJavaStringArray(env, count);
999
if (result) {
1000
BuildJavaArrayArgs args;
1001
args.result = result;
1002
args.env = env;
1003
args.used = 0;
1004
args.allowSlash = allowSlash;
1005
CFDictionaryApplyFunction(node, BuildJavaArrayFn, &args);
1006
if (!(*env)->ExceptionOccurred(env)) {
1007
// array construction succeeded
1008
if (args.used < count) {
1009
// finished array is smaller than expected.
1010
// Make a new array of precisely the right size.
1011
jarray newresult = createJavaStringArray(env, args.used);
1012
if (newresult) {
1013
JVM_ArrayCopy(env,0, result,0, newresult,0, args.used);
1014
result = newresult;
1015
}
1016
}
1017
}
1018
}
1019
1020
CFRelease(node);
1021
}
1022
1023
badparams:
1024
if (path) CFRelease(path);
1025
if (name) CFRelease(name);
1026
1027
return result;
1028
}
1029
1030
1031
JNIEXPORT jarray JNICALL
1032
Java_java_util_prefs_MacOSXPreferencesFile_getKeysForNode
1033
(JNIEnv *env, jobject klass, jobject jpath,
1034
jobject jname, jlong juser, jlong jhost)
1035
{
1036
return getStringsForNode(env, klass, jpath, jname, juser, jhost, false);
1037
}
1038
1039
JNIEXPORT jarray JNICALL
1040
Java_java_util_prefs_MacOSXPreferencesFile_getChildrenForNode
1041
(JNIEnv *env, jobject klass, jobject jpath,
1042
jobject jname, jlong juser, jlong jhost)
1043
{
1044
return getStringsForNode(env, klass, jpath, jname, juser, jhost, true);
1045
}
1046
1047
1048
// Returns false on error instead of throwing.
1049
JNIEXPORT jboolean JNICALL
1050
Java_java_util_prefs_MacOSXPreferencesFile_synchronize
1051
(JNIEnv *env, jobject klass,
1052
jstring jname, jlong juser, jlong jhost)
1053
{
1054
CFStringRef name = toCF(env, jname);
1055
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
1056
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
1057
jboolean result = 0;
1058
1059
if (name) {
1060
result = CFPreferencesSynchronize(name, user, host);
1061
CFRelease(name);
1062
}
1063
1064
return result;
1065
}
1066
1067