jdk

Форк
0
/
MacOSXPreferencesFile.m 
1065 строк · 33.2 Кб
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 jboolean 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
    jboolean beforeAdd = JNI_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) ? JNI_TRUE : JNI_FALSE;
696
    CFDictionaryAddValue(parent, child, node);
697
    if (!beforeAdd)
698
        beforeAdd = CFDictionaryContainsKey(parent, child) ? JNI_TRUE : JNI_FALSE;
699
    else
700
        beforeAdd = JNI_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

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.