1 | /* |
2 | * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. |
3 | */ |
4 | |
5 | /* Copyright (c) 2002 Graz University of Technology. All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions are met: |
9 | * |
10 | * 1. Redistributions of source code must retain the above copyright notice, |
11 | * this list of conditions and the following disclaimer. |
12 | * |
13 | * 2. Redistributions in binary form must reproduce the above copyright notice, |
14 | * this list of conditions and the following disclaimer in the documentation |
15 | * and/or other materials provided with the distribution. |
16 | * |
17 | * 3. The end-user documentation included with the redistribution, if any, must |
18 | * include the following acknowledgment: |
19 | * |
20 | * "This product includes software developed by IAIK of Graz University of |
21 | * Technology." |
22 | * |
23 | * Alternately, this acknowledgment may appear in the software itself, if |
24 | * and wherever such third-party acknowledgments normally appear. |
25 | * |
26 | * 4. The names "Graz University of Technology" and "IAIK of Graz University of |
27 | * Technology" must not be used to endorse or promote products derived from |
28 | * this software without prior written permission. |
29 | * |
30 | * 5. Products derived from this software may not be called |
31 | * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior |
32 | * written permission of Graz University of Technology. |
33 | * |
34 | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED |
35 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
36 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
37 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE |
38 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
39 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
40 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
41 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
42 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
43 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
44 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
45 | * POSSIBILITY OF SUCH DAMAGE. |
46 | */ |
47 | |
48 | #include "pkcs11wrapper.h" |
49 | |
50 | #include <stdio.h> |
51 | #include <stdlib.h> |
52 | #include <string.h> |
53 | #include <assert.h> |
54 | |
55 | #include "sun_security_pkcs11_wrapper_PKCS11.h" |
56 | |
57 | #ifdef P11_ENABLE_C_CREATEOBJECT |
58 | /* |
59 | * Class: sun_security_pkcs11_wrapper_PKCS11 |
60 | * Method: C_CreateObject |
61 | * Signature: (J[Lsun/security/pkcs11/wrapper/CK_ATTRIBUTE;)J |
62 | * Parametermapping: *PKCS11* |
63 | * @param jlong jSessionHandle CK_SESSION_HANDLE hSession |
64 | * @param jobjectArray jTemplate CK_ATTRIBUTE_PTR pTemplate |
65 | * CK_ULONG ulCount |
66 | * @return jlong jObjectHandle CK_OBJECT_HANDLE_PTR phObject |
67 | */ |
68 | JNIEXPORT jlong JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1CreateObject |
69 | (JNIEnv *env, jobject obj, jlong jSessionHandle, jobjectArray jTemplate) |
70 | { |
71 | CK_SESSION_HANDLE ckSessionHandle; |
72 | CK_OBJECT_HANDLE ckObjectHandle; |
73 | CK_ATTRIBUTE_PTR ckpAttributes = NULL_PTR; |
74 | CK_ULONG ckAttributesLength; |
75 | jlong jObjectHandle = 0L; |
76 | CK_RV rv; |
77 | |
78 | CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); |
79 | if (ckpFunctions == NULL) { return 0L; } |
80 | |
81 | ckSessionHandle = jLongToCKULong(jSessionHandle); |
82 | jAttributeArrayToCKAttributeArray(env, jTemplate, &ckpAttributes, &ckAttributesLength); |
83 | if ((*env)->ExceptionCheck(env)) { return 0L; } |
84 | |
85 | rv = (*ckpFunctions->C_CreateObject)(ckSessionHandle, ckpAttributes, ckAttributesLength, &ckObjectHandle); |
86 | |
87 | jObjectHandle = ckULongToJLong(ckObjectHandle); |
88 | freeCKAttributeArray(ckpAttributes, ckAttributesLength); |
89 | |
90 | if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return 0L ; } |
91 | |
92 | return jObjectHandle ; |
93 | } |
94 | #endif |
95 | |
96 | #ifdef P11_ENABLE_C_COPYOBJECT |
97 | /* |
98 | * Class: sun_security_pkcs11_wrapper_PKCS11 |
99 | * Method: C_CopyObject |
100 | * Signature: (JJ[Lsun/security/pkcs11/wrapper/CK_ATTRIBUTE;)J |
101 | * Parametermapping: *PKCS11* |
102 | * @param jlong jSessionHandle CK_SESSION_HANDLE hSession |
103 | * @param jlong jObjectHandle CK_OBJECT_HANDLE hObject |
104 | * @param jobjectArray jTemplate CK_ATTRIBUTE_PTR pTemplate |
105 | * CK_ULONG ulCount |
106 | * @return jlong jNewObjectHandle CK_OBJECT_HANDLE_PTR phNewObject |
107 | */ |
108 | JNIEXPORT jlong JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1CopyObject |
109 | (JNIEnv *env, jobject obj, jlong jSessionHandle, jlong jObjectHandle, jobjectArray jTemplate) |
110 | { |
111 | CK_SESSION_HANDLE ckSessionHandle; |
112 | CK_OBJECT_HANDLE ckObjectHandle; |
113 | CK_OBJECT_HANDLE ckNewObjectHandle; |
114 | CK_ATTRIBUTE_PTR ckpAttributes = NULL_PTR; |
115 | CK_ULONG ckAttributesLength; |
116 | jlong jNewObjectHandle = 0L; |
117 | CK_RV rv; |
118 | |
119 | CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); |
120 | if (ckpFunctions == NULL) { return 0L; } |
121 | |
122 | ckSessionHandle = jLongToCKULong(jSessionHandle); |
123 | ckObjectHandle = jLongToCKULong(jObjectHandle); |
124 | jAttributeArrayToCKAttributeArray(env, jTemplate, &ckpAttributes, &ckAttributesLength); |
125 | if ((*env)->ExceptionCheck(env)) { return 0L; } |
126 | |
127 | rv = (*ckpFunctions->C_CopyObject)(ckSessionHandle, ckObjectHandle, ckpAttributes, ckAttributesLength, &ckNewObjectHandle); |
128 | |
129 | jNewObjectHandle = ckULongToJLong(ckNewObjectHandle); |
130 | freeCKAttributeArray(ckpAttributes, ckAttributesLength); |
131 | |
132 | if(ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return 0L ; } |
133 | |
134 | return jNewObjectHandle ; |
135 | } |
136 | #endif |
137 | |
138 | #ifdef P11_ENABLE_C_DESTROYOBJECT |
139 | /* |
140 | * Class: sun_security_pkcs11_wrapper_PKCS11 |
141 | * Method: C_DestroyObject |
142 | * Signature: (JJ)V |
143 | * Parametermapping: *PKCS11* |
144 | * @param jlong jSessionHandle CK_SESSION_HANDLE hSession |
145 | * @param jlong jObjectHandle CK_OBJECT_HANDLE hObject |
146 | */ |
147 | JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1DestroyObject |
148 | (JNIEnv *env, jobject obj, jlong jSessionHandle, jlong jObjectHandle) |
149 | { |
150 | CK_SESSION_HANDLE ckSessionHandle; |
151 | CK_OBJECT_HANDLE ckObjectHandle; |
152 | CK_RV rv; |
153 | |
154 | CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); |
155 | if (ckpFunctions == NULL) { return; } |
156 | |
157 | ckSessionHandle = jLongToCKULong(jSessionHandle); |
158 | ckObjectHandle = jLongToCKULong(jObjectHandle); |
159 | |
160 | rv = (*ckpFunctions->C_DestroyObject)(ckSessionHandle, ckObjectHandle); |
161 | if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; } |
162 | } |
163 | #endif |
164 | |
165 | #ifdef P11_ENABLE_C_GETOBJECTSIZE |
166 | /* |
167 | * Class: sun_security_pkcs11_wrapper_PKCS11 |
168 | * Method: C_GetObjectSize |
169 | * Signature: (JJ)J |
170 | * Parametermapping: *PKCS11* |
171 | * @param jlong jSessionHandle CK_SESSION_HANDLE hSession |
172 | * @param jlong jObjectHandle CK_OBJECT_HANDLE hObject |
173 | * @return jlong jObjectSize CK_ULONG_PTR pulSize |
174 | */ |
175 | JNIEXPORT jlong JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1GetObjectSize |
176 | (JNIEnv *env, jobject obj, jlong jSessionHandle, jlong jObjectHandle) |
177 | { |
178 | CK_SESSION_HANDLE ckSessionHandle; |
179 | CK_OBJECT_HANDLE ckObjectHandle; |
180 | CK_ULONG ckObjectSize; |
181 | jlong jObjectSize = 0L; |
182 | CK_RV rv; |
183 | |
184 | CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); |
185 | if (ckpFunctions == NULL) { return 0L; } |
186 | |
187 | ckSessionHandle = jLongToCKULong(jSessionHandle); |
188 | ckObjectHandle = jLongToCKULong(jObjectHandle); |
189 | |
190 | rv = (*ckpFunctions->C_GetObjectSize)(ckSessionHandle, ckObjectHandle, &ckObjectSize); |
191 | if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return 0L ; } |
192 | |
193 | jObjectSize = ckULongToJLong(ckObjectSize); |
194 | |
195 | return jObjectSize ; |
196 | } |
197 | #endif |
198 | |
199 | #ifdef P11_ENABLE_C_GETATTRIBUTEVALUE |
200 | /* |
201 | * Class: sun_security_pkcs11_wrapper_PKCS11 |
202 | * Method: C_GetAttributeValue |
203 | * Signature: (JJ[Lsun/security/pkcs11/wrapper/CK_ATTRIBUTE;)[Lsun/security/pkcs11/wrapper/CK_ATTRIBUTE; |
204 | * Parametermapping: *PKCS11* |
205 | * @param jlong jSessionHandle CK_SESSION_HANDLE hSession |
206 | * @param jlong jObjectHandle CK_OBJECT_HANDLE hObject |
207 | * @param jobjectArray jTemplate CK_ATTRIBUTE_PTR pTemplate |
208 | * CK_ULONG ulCount |
209 | */ |
210 | JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1GetAttributeValue |
211 | (JNIEnv *env, jobject obj, jlong jSessionHandle, jlong jObjectHandle, jobjectArray jTemplate) |
212 | { |
213 | CK_SESSION_HANDLE ckSessionHandle; |
214 | CK_OBJECT_HANDLE ckObjectHandle; |
215 | CK_ATTRIBUTE_PTR ckpAttributes = NULL_PTR; |
216 | CK_ULONG ckAttributesLength; |
217 | CK_ULONG ckBufferLength; |
218 | CK_ULONG i; |
219 | jobject jAttribute; |
220 | CK_RV rv; |
221 | |
222 | CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); |
223 | if (ckpFunctions == NULL) { return; } |
224 | |
225 | TRACE0("DEBUG: C_GetAttributeValue" ); |
226 | TRACE1(", hSession=%u" , jSessionHandle); |
227 | TRACE1(", hObject=%u" , jObjectHandle); |
228 | TRACE1(", pTemplate=%p" , jTemplate); |
229 | TRACE0(" ... " ); |
230 | |
231 | ckSessionHandle = jLongToCKULong(jSessionHandle); |
232 | ckObjectHandle = jLongToCKULong(jObjectHandle); |
233 | TRACE1("jAttributeArrayToCKAttributeArray now with jTemplate = %d" , jTemplate); |
234 | jAttributeArrayToCKAttributeArray(env, jTemplate, &ckpAttributes, &ckAttributesLength); |
235 | if ((*env)->ExceptionCheck(env)) { return; } |
236 | |
237 | TRACE2("DEBUG: jAttributeArrayToCKAttributeArray finished with ckpAttribute = %d, Length = %d\n" , ckpAttributes, ckAttributesLength); |
238 | |
239 | /* first set all pValue to NULL, to get the needed buffer length */ |
240 | for(i = 0; i < ckAttributesLength; i++) { |
241 | if (ckpAttributes[i].pValue != NULL_PTR) { |
242 | free(ckpAttributes[i].pValue); |
243 | ckpAttributes[i].pValue = NULL_PTR; |
244 | } |
245 | } |
246 | |
247 | rv = (*ckpFunctions->C_GetAttributeValue)(ckSessionHandle, ckObjectHandle, ckpAttributes, ckAttributesLength); |
248 | if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { |
249 | free(ckpAttributes); |
250 | return ; |
251 | } |
252 | |
253 | /* now, the ulValueLength field of each attribute should hold the exact buffer length needed |
254 | * allocate the needed buffers accordingly |
255 | */ |
256 | for (i = 0; i < ckAttributesLength; i++) { |
257 | ckBufferLength = sizeof(CK_BYTE) * ckpAttributes[i].ulValueLen; |
258 | ckpAttributes[i].pValue = (void *) malloc(ckBufferLength); |
259 | if (ckpAttributes[i].pValue == NULL) { |
260 | freeCKAttributeArray(ckpAttributes, i); |
261 | throwOutOfMemoryError(env, 0); |
262 | return; |
263 | } |
264 | ckpAttributes[i].ulValueLen = ckBufferLength; |
265 | } |
266 | |
267 | /* now get the attributes with all values */ |
268 | rv = (*ckpFunctions->C_GetAttributeValue)(ckSessionHandle, ckObjectHandle, ckpAttributes, ckAttributesLength); |
269 | |
270 | if (ckAssertReturnValueOK(env, rv) == CK_ASSERT_OK) { |
271 | /* copy back the values to the Java attributes */ |
272 | for (i = 0; i < ckAttributesLength; i++) { |
273 | jAttribute = ckAttributePtrToJAttribute(env, &(ckpAttributes[i])); |
274 | if (jAttribute == NULL) { |
275 | freeCKAttributeArray(ckpAttributes, ckAttributesLength); |
276 | return; |
277 | } |
278 | (*env)->SetObjectArrayElement(env, jTemplate, i, jAttribute); |
279 | if ((*env)->ExceptionCheck(env)) { |
280 | freeCKAttributeArray(ckpAttributes, ckAttributesLength); |
281 | return; |
282 | } |
283 | } |
284 | } |
285 | freeCKAttributeArray(ckpAttributes, ckAttributesLength); |
286 | TRACE0("FINISHED\n" ); |
287 | } |
288 | #endif |
289 | |
290 | #ifdef P11_ENABLE_C_SETATTRIBUTEVALUE |
291 | /* |
292 | * Class: sun_security_pkcs11_wrapper_PKCS11 |
293 | * Method: C_SetAttributeValue |
294 | * Signature: (JJ[Lsun/security/pkcs11/wrapper/CK_ATTRIBUTE;)V |
295 | * Parametermapping: *PKCS11* |
296 | * @param jlong jSessionHandle CK_SESSION_HANDLE hSession |
297 | * @param jlong jObjectHandle CK_OBJECT_HANDLE hObject |
298 | * @param jobjectArray jTemplate CK_ATTRIBUTE_PTR pTemplate |
299 | * CK_ULONG ulCount |
300 | */ |
301 | JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1SetAttributeValue |
302 | (JNIEnv *env, jobject obj, jlong jSessionHandle, jlong jObjectHandle, jobjectArray jTemplate) |
303 | { |
304 | CK_SESSION_HANDLE ckSessionHandle; |
305 | CK_OBJECT_HANDLE ckObjectHandle; |
306 | CK_ATTRIBUTE_PTR ckpAttributes = NULL_PTR; |
307 | CK_ULONG ckAttributesLength; |
308 | CK_RV rv; |
309 | |
310 | CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); |
311 | if (ckpFunctions == NULL) { return; } |
312 | |
313 | ckSessionHandle = jLongToCKULong(jSessionHandle); |
314 | ckObjectHandle = jLongToCKULong(jObjectHandle); |
315 | jAttributeArrayToCKAttributeArray(env, jTemplate, &ckpAttributes, &ckAttributesLength); |
316 | if ((*env)->ExceptionCheck(env)) { return; } |
317 | |
318 | rv = (*ckpFunctions->C_SetAttributeValue)(ckSessionHandle, ckObjectHandle, ckpAttributes, ckAttributesLength); |
319 | |
320 | freeCKAttributeArray(ckpAttributes, ckAttributesLength); |
321 | |
322 | if(ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; } |
323 | } |
324 | #endif |
325 | |
326 | #ifdef P11_ENABLE_C_FINDOBJECTSINIT |
327 | /* |
328 | * Class: sun_security_pkcs11_wrapper_PKCS11 |
329 | * Method: C_FindObjectsInit |
330 | * Signature: (J[Lsun/security/pkcs11/wrapper/CK_ATTRIBUTE;)V |
331 | * Parametermapping: *PKCS11* |
332 | * @param jlong jSessionHandle CK_SESSION_HANDLE hSession |
333 | * @param jobjectArray jTemplate CK_ATTRIBUTE_PTR pTemplate |
334 | * CK_ULONG ulCount |
335 | */ |
336 | JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1FindObjectsInit |
337 | (JNIEnv *env, jobject obj, jlong jSessionHandle, jobjectArray jTemplate) |
338 | { |
339 | CK_SESSION_HANDLE ckSessionHandle; |
340 | CK_ATTRIBUTE_PTR ckpAttributes = NULL_PTR; |
341 | CK_ULONG ckAttributesLength; |
342 | CK_RV rv; |
343 | |
344 | CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); |
345 | if (ckpFunctions == NULL) { return; } |
346 | |
347 | TRACE0("DEBUG: C_FindObjectsInit" ); |
348 | TRACE1(", hSession=%u" , jSessionHandle); |
349 | TRACE1(", pTemplate=%p" , jTemplate); |
350 | TRACE0(" ... " ); |
351 | |
352 | ckSessionHandle = jLongToCKULong(jSessionHandle); |
353 | jAttributeArrayToCKAttributeArray(env, jTemplate, &ckpAttributes, &ckAttributesLength); |
354 | if ((*env)->ExceptionCheck(env)) { return; } |
355 | |
356 | rv = (*ckpFunctions->C_FindObjectsInit)(ckSessionHandle, ckpAttributes, ckAttributesLength); |
357 | |
358 | freeCKAttributeArray(ckpAttributes, ckAttributesLength); |
359 | TRACE0("FINISHED\n" ); |
360 | |
361 | if(ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; } |
362 | } |
363 | #endif |
364 | |
365 | #ifdef P11_ENABLE_C_FINDOBJECTS |
366 | /* |
367 | * Class: sun_security_pkcs11_wrapper_PKCS11 |
368 | * Method: C_FindObjects |
369 | * Signature: (JJ)[J |
370 | * Parametermapping: *PKCS11* |
371 | * @param jlong jSessionHandle CK_SESSION_HANDLE hSession |
372 | * @param jlong jMaxObjectCount CK_ULONG ulMaxObjectCount |
373 | * @return jlongArray jObjectHandleArray CK_OBJECT_HANDLE_PTR phObject |
374 | * CK_ULONG_PTR pulObjectCount |
375 | */ |
376 | JNIEXPORT jlongArray JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1FindObjects |
377 | (JNIEnv *env, jobject obj, jlong jSessionHandle, jlong jMaxObjectCount) |
378 | { |
379 | CK_RV rv; |
380 | CK_SESSION_HANDLE ckSessionHandle; |
381 | CK_ULONG ckMaxObjectLength; |
382 | CK_OBJECT_HANDLE_PTR ckpObjectHandleArray; |
383 | CK_ULONG ckActualObjectCount; |
384 | jlongArray jObjectHandleArray = NULL; |
385 | |
386 | CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); |
387 | if (ckpFunctions == NULL) { return NULL; } |
388 | |
389 | ckSessionHandle = jLongToCKULong(jSessionHandle); |
390 | ckMaxObjectLength = jLongToCKULong(jMaxObjectCount); |
391 | ckpObjectHandleArray = (CK_OBJECT_HANDLE_PTR) malloc(sizeof(CK_OBJECT_HANDLE) * ckMaxObjectLength); |
392 | if (ckpObjectHandleArray == NULL) { |
393 | throwOutOfMemoryError(env, 0); |
394 | return NULL; |
395 | } |
396 | |
397 | rv = (*ckpFunctions->C_FindObjects)(ckSessionHandle, ckpObjectHandleArray, ckMaxObjectLength, &ckActualObjectCount); |
398 | if (ckAssertReturnValueOK(env, rv) == CK_ASSERT_OK) { |
399 | jObjectHandleArray = ckULongArrayToJLongArray(env, ckpObjectHandleArray, ckActualObjectCount); |
400 | } |
401 | |
402 | free(ckpObjectHandleArray); |
403 | |
404 | return jObjectHandleArray ; |
405 | } |
406 | #endif |
407 | |
408 | #ifdef P11_ENABLE_C_FINDOBJECTSFINAL |
409 | /* |
410 | * Class: sun_security_pkcs11_wrapper_PKCS11 |
411 | * Method: C_FindObjectsFinal |
412 | * Signature: (J)V |
413 | * Parametermapping: *PKCS11* |
414 | * @param jlong jSessionHandle CK_SESSION_HANDLE hSession |
415 | */ |
416 | JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1FindObjectsFinal |
417 | (JNIEnv *env, jobject obj, jlong jSessionHandle) |
418 | { |
419 | CK_SESSION_HANDLE ckSessionHandle; |
420 | CK_RV rv; |
421 | |
422 | CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); |
423 | if (ckpFunctions == NULL) { return; } |
424 | |
425 | ckSessionHandle = jLongToCKULong(jSessionHandle); |
426 | rv = (*ckpFunctions->C_FindObjectsFinal)(ckSessionHandle); |
427 | if(ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; } |
428 | } |
429 | #endif |
430 | |