1 |
commit: e2c4faef071d1e2210ea330d5f14f1fe80403527 |
2 |
Author: Amy Liffey <amynka <AT> gentoo <DOT> org> |
3 |
AuthorDate: Thu Aug 15 19:14:35 2019 +0000 |
4 |
Commit: Amy Liffey <amynka <AT> gentoo <DOT> org> |
5 |
CommitDate: Thu Aug 15 19:19:35 2019 +0000 |
6 |
URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=e2c4faef |
7 |
|
8 |
app-crypt/eid-mw: version bump 4.4.19 |
9 |
|
10 |
Closes: https://bugs.gentoo.org/690038 |
11 |
Submitted-by: Vincent Hardy <vincent.hardy.be <AT> gmail.com> |
12 |
Package-Manager: Portage-2.3.69, Repoman-2.3.16 |
13 |
Signed-off-by: Amy Liffey <amynka <AT> gentoo.org> |
14 |
|
15 |
app-crypt/eid-mw/Manifest | 1 + |
16 |
app-crypt/eid-mw/eid-mw-4.4.19.ebuild | 103 ++++++++ |
17 |
app-crypt/eid-mw/files/eid-sign-test-4.4.19.patch | 272 ++++++++++++++++++++++ |
18 |
3 files changed, 376 insertions(+) |
19 |
|
20 |
diff --git a/app-crypt/eid-mw/Manifest b/app-crypt/eid-mw/Manifest |
21 |
index debf3f3f29b..644dc3133d4 100644 |
22 |
--- a/app-crypt/eid-mw/Manifest |
23 |
+++ b/app-crypt/eid-mw/Manifest |
24 |
@@ -1,2 +1,3 @@ |
25 |
DIST eid-mw-4.4.1.tar.gz 7708697 BLAKE2B 7bdbaa37af1cdd5a8abd1519939f663c7c871cea49850932d97c391465de362885b061c024285ab2690473a4bb1ba57a70fdc12a720246b2a610ac9525f6395e SHA512 9de54858ac052eeff101070dd11c5cf60ab1d29b8dc8d946680745acf9aa6ba1044ca5451eca6066b5b467fbc32a2a23bbe7e8551ca7559e6003a4097a304cfc |
26 |
DIST eid-mw-4.4.11.tar.gz 7975630 BLAKE2B e7b052736cba98c53e97e9de15ea8e54f508c28471a7a936c5d28539648f5d3454983d9c9a21cf5959a070849865de5ca58d5d29ad64eb08209afd40959c56ae SHA512 35273492abc0efa7146848c82770bcd5e4ce1b7c9971ce45686b4d3244332456011db6df05154ad6a204854fd13ac3dce8f7b8cd6112a2e60a7b8434f3c4a196 |
27 |
+DIST eid-mw-4.4.19.tar.gz 7941144 BLAKE2B c02f975081eacf7803839461ae91e3c6a24c3e2fc60064a981cc71f629925679b0df997e8acbe2da2f1e58dcc665b8633e3c9bf28b07be90ac8cdd0448e80848 SHA512 21530111461302123be662dd60f3fbd56ffd8a0537cac88c99d5a6bcd9163a27bc34e05beaf7502dd5ada247ca324d21590a866907b5a7a02ce3d8d78fcac634 |
28 |
|
29 |
diff --git a/app-crypt/eid-mw/eid-mw-4.4.19.ebuild b/app-crypt/eid-mw/eid-mw-4.4.19.ebuild |
30 |
new file mode 100644 |
31 |
index 00000000000..98c8bfee6ce |
32 |
--- /dev/null |
33 |
+++ b/app-crypt/eid-mw/eid-mw-4.4.19.ebuild |
34 |
@@ -0,0 +1,103 @@ |
35 |
+# Copyright 1999-2019 Gentoo Authors |
36 |
+# Distributed under the terms of the GNU General Public License v2 |
37 |
+ |
38 |
+EAPI=7 |
39 |
+ |
40 |
+inherit autotools desktop gnome2-utils xdg-utils |
41 |
+ |
42 |
+DESCRIPTION="Electronic Identity Card middleware supplied by the Belgian Federal Government" |
43 |
+HOMEPAGE="https://eid.belgium.be" |
44 |
+SRC_URI="https://codeload.github.com/fedict/${PN}/tar.gz/v${PV} -> ${P}.tar.gz" |
45 |
+ |
46 |
+LICENSE="LGPL-3" |
47 |
+SLOT="0" |
48 |
+KEYWORDS="~amd64 ~arm ~x86" |
49 |
+IUSE="+dialogs +gtk +p11v220 p11-kit" |
50 |
+ |
51 |
+RDEPEND=">=sys-apps/pcsc-lite-1.2.9 |
52 |
+ gtk? ( |
53 |
+ x11-libs/gdk-pixbuf[jpeg] |
54 |
+ x11-libs/gtk+:* |
55 |
+ dev-libs/libxml2 |
56 |
+ net-misc/curl[ssl] |
57 |
+ net-libs/libproxy |
58 |
+ !app-misc/eid-viewer-bin |
59 |
+ ) |
60 |
+ p11-kit? ( app-crypt/p11-kit )" |
61 |
+ |
62 |
+DEPEND="${RDEPEND} |
63 |
+ virtual/pkgconfig" |
64 |
+ |
65 |
+REQUIRED_USE="dialogs? ( gtk )" |
66 |
+ |
67 |
+src_prepare() { |
68 |
+ default |
69 |
+ |
70 |
+ sed -i -e 's:/beid/rsaref220:/rsaref220:' configure.ac || die |
71 |
+ sed -i -e 's:/beid::' cardcomm/pkcs11/src/libbeidpkcs11.pc.in || die |
72 |
+ |
73 |
+ # Buggy internal versioning when autoreconf a tarball release. |
74 |
+ # Weird numbering is required otherwise we get a seg fault in |
75 |
+ # about-eid-mw program. |
76 |
+ echo "${PV}-v${PV}" > .version |
77 |
+ sed -i \ |
78 |
+ -e '/^GITDESC/ d' \ |
79 |
+ -e '/^VERCLEAN/ d' \ |
80 |
+ scripts/build-aux/genver.sh |
81 |
+ |
82 |
+ # legacy xpi module : we don't want it anymore |
83 |
+ sed -i -e '/SUBDIRS/ s:plugins_tools/xpi ::' Makefile.am || die |
84 |
+ sed -i -e '/plugins_tools\/xpi/ d' configure.ac || die |
85 |
+ |
86 |
+ # hardcoded lsb_info |
87 |
+ sed -i \ |
88 |
+ -e "s:get_lsb_info('i'):strdup(_(\"Gentoo\")):" \ |
89 |
+ -e "s:get_lsb_info('r'):strdup(_(\"n/a\")):" \ |
90 |
+ -e "s:get_lsb_info('c'):strdup(_(\"n/a\")):" \ |
91 |
+ plugins_tools/aboutmw/gtk/about-main.c || die |
92 |
+ |
93 |
+ # Fix libdir for pkcs11_manifestdir |
94 |
+ sed -i \ |
95 |
+ -e "/pkcs11_manifestdir/ s:prefix)/lib:libdir):" \ |
96 |
+ cardcomm/pkcs11/src/Makefile.am || die |
97 |
+ |
98 |
+ # See bug #691308 |
99 |
+ eapply "${FILESDIR}/eid-sign-test-${PV}.patch" |
100 |
+ |
101 |
+ eautoreconf |
102 |
+} |
103 |
+ |
104 |
+src_configure() { |
105 |
+ econf \ |
106 |
+ $(use_enable dialogs) \ |
107 |
+ $(use_enable p11v220) \ |
108 |
+ $(use_enable p11-kit p11kit) \ |
109 |
+ $(use_with gtk gtkvers 'detect') \ |
110 |
+ --with-gnu-ld \ |
111 |
+ --disable-static |
112 |
+} |
113 |
+ |
114 |
+src_install() { |
115 |
+ default |
116 |
+ rm -r "${ED}"/usr/$(get_libdir)/*.la || die |
117 |
+ if use gtk; then |
118 |
+ domenu plugins_tools/eid-viewer/eid-viewer.desktop |
119 |
+ doicon plugins_tools/eid-viewer/gtk/eid-viewer.png |
120 |
+ fi |
121 |
+} |
122 |
+ |
123 |
+pkg_postinst(){ |
124 |
+ if use gtk; then |
125 |
+ gnome2_schemas_update |
126 |
+ xdg_desktop_database_update |
127 |
+ xdg_icon_cache_update |
128 |
+ fi |
129 |
+} |
130 |
+ |
131 |
+pkg_postrm(){ |
132 |
+ if use gtk; then |
133 |
+ gnome2_schemas_update |
134 |
+ xdg_desktop_database_update |
135 |
+ xdg_icon_cache_update |
136 |
+ fi |
137 |
+} |
138 |
|
139 |
diff --git a/app-crypt/eid-mw/files/eid-sign-test-4.4.19.patch b/app-crypt/eid-mw/files/eid-sign-test-4.4.19.patch |
140 |
new file mode 100644 |
141 |
index 00000000000..cf59f99a8ea |
142 |
--- /dev/null |
143 |
+++ b/app-crypt/eid-mw/files/eid-sign-test-4.4.19.patch |
144 |
@@ -0,0 +1,272 @@ |
145 |
+--- eid-mw-4.4.19/tests/unit/sign.c 2019-07-11 16:08:46.000000000 +0200 |
146 |
++++ eid-mw-git/tests/unit/sign.c 2019-08-04 17:40:08.683942928 +0200 |
147 |
+@@ -19,10 +19,13 @@ |
148 |
+ **************************************************************************** */ |
149 |
+ #ifdef WIN32 |
150 |
+ #include <win32.h> |
151 |
++#pragma pack(push, cryptoki, 1) |
152 |
++#include "pkcs11.h" |
153 |
++#pragma pack(pop, cryptoki) |
154 |
+ #else |
155 |
+ #include <unix.h> |
156 |
+-#endif |
157 |
+ #include <pkcs11.h> |
158 |
++#endif |
159 |
+ #include <stdio.h> |
160 |
+ #include <string.h> |
161 |
+ #include <stdlib.h> |
162 |
+@@ -33,66 +36,88 @@ |
163 |
+ #include <config.h> |
164 |
+ #endif |
165 |
+ |
166 |
++#include <stdbool.h> |
167 |
++ |
168 |
+ #if HAVE_OPENSSL |
169 |
+-#include <openssl/rsa.h> |
170 |
++#include <openssl/opensslv.h> |
171 |
++#include <openssl/evp.h> |
172 |
+ #include <openssl/engine.h> |
173 |
+ |
174 |
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
175 |
+-static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) { |
176 |
+- if(!r || !n || !e) { |
177 |
+- return 0; |
178 |
+- } |
179 |
+- r->n = n; |
180 |
+- r->e = e; |
181 |
+- r->d = d; |
182 |
+- return 1; |
183 |
+-} |
184 |
+-#endif |
185 |
+- |
186 |
+-CK_BYTE digest_sha256[] = { |
187 |
+- 0x2c, 0x26, 0xb4, 0x6b, |
188 |
+- 0x68, 0xff, 0xc6, 0x8f, |
189 |
+- 0xf9, 0x9b, 0x45, 0x3c, |
190 |
+- 0x1d, 0x30, 0x41, 0x34, |
191 |
+- 0x13, 0x42, 0x2d, 0x70, |
192 |
+- 0x64, 0x83, 0xbf, 0xa0, |
193 |
+- 0xf9, 0x8a, 0x5e, 0x88, |
194 |
+- 0x62, 0x66, 0xe7, 0xae |
195 |
+-}; |
196 |
+- |
197 |
+-int verify_sig(unsigned char* sig, CK_ULONG siglen, CK_BYTE_PTR modulus, CK_ULONG modlen, CK_BYTE_PTR exponent, CK_ULONG explen) { |
198 |
+- RSA* rsa = RSA_new(); |
199 |
+- unsigned char* s = malloc(siglen); |
200 |
+- int ret; |
201 |
+- |
202 |
+- RSA_set0_key(rsa, BN_bin2bn(modulus, (int) modlen, NULL), BN_bin2bn(exponent, (int) explen, NULL), NULL); |
203 |
+- |
204 |
+- int v = RSA_verify(NID_sha256, digest_sha256, sizeof(digest_sha256), sig, siglen, rsa); |
205 |
+- |
206 |
+- printf("Signature verification returned: %d\n", v); |
207 |
+- if(!v) { |
208 |
+- unsigned long e = ERR_get_error(); |
209 |
+- printf("error %ld: %s\n", e, ERR_error_string(e, NULL)); |
210 |
+- ret = TEST_RV_FAIL; |
211 |
+- } else { |
212 |
+- ret = TEST_RV_OK; |
213 |
++// These were copied from eid-test-ca:derencode.c |
214 |
++int verify_sig(const unsigned char *sig_in, CK_ULONG siglen, const unsigned char *certificate, size_t certlen, bool is_rsa) { |
215 |
++#if OPENSSL_VERSION_NUMBER > 0x10100000L |
216 |
++ X509 *cert = NULL; |
217 |
++ EVP_PKEY *pkey = NULL; |
218 |
++ EVP_MD_CTX *mdctx; |
219 |
++ EVP_PKEY_CTX *pctx; |
220 |
++ const EVP_MD *md = EVP_get_digestbyname("sha256"); |
221 |
++ unsigned char *sig = (unsigned char*)sig_in; |
222 |
++ |
223 |
++ if(d2i_X509(&cert, &certificate, certlen) == NULL) { |
224 |
++ fprintf(stderr, "E: could not parse X509 certificate\n"); |
225 |
++ return TEST_RV_FAIL; |
226 |
++ } |
227 |
++ pkey = X509_get0_pubkey(cert); |
228 |
++ if(pkey == NULL) { |
229 |
++ fprintf(stderr, "E: could not find public key in certificate\n"); |
230 |
++ return TEST_RV_FAIL; |
231 |
++ } |
232 |
++ mdctx = EVP_MD_CTX_new(); |
233 |
++ if(EVP_DigestVerifyInit(mdctx, &pctx, md, NULL, pkey) != 1) { |
234 |
++ fprintf(stderr, "E: initialization for signature validation failed!\n"); |
235 |
++ return TEST_RV_FAIL; |
236 |
++ } |
237 |
++ if(EVP_DigestVerifyUpdate(mdctx, (const unsigned char*)"foo", 3) != 1) { |
238 |
++ fprintf(stderr, "E: hashing for signature failed!\n"); |
239 |
++ return TEST_RV_FAIL; |
240 |
+ } |
241 |
+ |
242 |
+- free(s); |
243 |
+- RSA_free(rsa); |
244 |
+- |
245 |
+- return ret; |
246 |
++ ECDSA_SIG* ec_sig; |
247 |
++ if(!is_rsa) { |
248 |
++ BIGNUM *r; |
249 |
++ BIGNUM *s; |
250 |
++ ec_sig = ECDSA_SIG_new(); |
251 |
++ if((r = BN_bin2bn(sig, siglen / 2, NULL)) == NULL) { |
252 |
++ fprintf(stderr, "E: could not convert R part of ECDSA signature!\n"); |
253 |
++ return TEST_RV_FAIL; |
254 |
++ } |
255 |
++ if((s = BN_bin2bn(sig + (siglen / 2), siglen / 2, NULL)) == NULL) { |
256 |
++ fprintf(stderr, "E: could not convert S part of ECDSA signature!\n"); |
257 |
++ return TEST_RV_FAIL; |
258 |
++ } |
259 |
++ if(ECDSA_SIG_set0(ec_sig, r, s) == 0) { |
260 |
++ fprintf(stderr, "E: could not set ECDSA_SIG structure!\n"); |
261 |
++ return TEST_RV_FAIL; |
262 |
++ } |
263 |
++ siglen = i2d_ECDSA_SIG(ec_sig, NULL); |
264 |
++ unsigned char *dersig = sig = malloc(siglen); |
265 |
++ siglen = i2d_ECDSA_SIG(ec_sig, &dersig); |
266 |
++ } |
267 |
++ if(EVP_DigestVerifyFinal(mdctx, sig, siglen) != 1) { |
268 |
++ fprintf(stderr, "E: signature fails validation!\n"); |
269 |
++ return TEST_RV_FAIL; |
270 |
++ } |
271 |
++ if(!is_rsa) { |
272 |
++ free(sig); |
273 |
++ } |
274 |
++ printf("signature verified\n"); |
275 |
++ return TEST_RV_OK; |
276 |
++#else |
277 |
++ printf("OpenSSL too old for verification\n"); |
278 |
++#endif |
279 |
+ } |
280 |
+- |
281 |
+ #endif |
282 |
+ |
283 |
+-int test_key(char* label, CK_SESSION_HANDLE session, CK_SLOT_ID slot EIDT_UNUSED) { |
284 |
++int test_key(char* label, CK_SESSION_HANDLE session, CK_SLOT_ID slot) { |
285 |
+ CK_ATTRIBUTE attr[2]; |
286 |
+ CK_MECHANISM mech; |
287 |
++ CK_MECHANISM_TYPE_PTR mechlist; |
288 |
+ CK_BYTE data[] = { 'f', 'o', 'o' }; |
289 |
+ CK_BYTE_PTR sig, mod, exp; |
290 |
+ CK_ULONG sig_len, type, count; |
291 |
+- CK_OBJECT_HANDLE privatekey, publickey; |
292 |
++ CK_OBJECT_HANDLE privatekey, publickey, certificate; |
293 |
++ bool is_rsa = false; |
294 |
++ int i; |
295 |
+ |
296 |
+ attr[0].type = CKA_CLASS; |
297 |
+ attr[0].pValue = &type; |
298 |
+@@ -113,7 +138,22 @@ |
299 |
+ return TEST_RV_SKIP; |
300 |
+ } |
301 |
+ |
302 |
+- mech.mechanism = CKM_SHA256_RSA_PKCS; |
303 |
++ check_rv(C_GetMechanismList(slot, NULL_PTR, &count)); |
304 |
++ mechlist = malloc(sizeof(CK_MECHANISM_TYPE) * count); |
305 |
++#undef CHECK_RV_DEALLOCATE |
306 |
++#define CHECK_RV_DEALLOCATE free(mechlist) |
307 |
++ |
308 |
++ check_rv(C_GetMechanismList(slot, mechlist, &count)); |
309 |
++ |
310 |
++ for(i=0; i<count; i++) { |
311 |
++ if(mechlist[i] == CKM_SHA256_RSA_PKCS) { |
312 |
++ mech.mechanism = mechlist[i]; |
313 |
++ i=count; |
314 |
++ is_rsa = true; |
315 |
++ break; |
316 |
++ } |
317 |
++ } |
318 |
++ |
319 |
+ check_rv(C_SignInit(session, &mech, privatekey)); |
320 |
+ |
321 |
+ check_rv(C_Sign(session, data, sizeof(data), NULL, &sig_len)); |
322 |
+@@ -124,42 +164,68 @@ |
323 |
+ |
324 |
+ hex_dump((char*)sig, sig_len); |
325 |
+ |
326 |
+- type = CKO_PUBLIC_KEY; |
327 |
+- check_rv(C_FindObjectsInit(session, attr, 2)); |
328 |
+- check_rv(C_FindObjects(session, &publickey, 1, &count)); |
329 |
+- verbose_assert(count == 1); |
330 |
+- check_rv(C_FindObjectsFinal(session)); |
331 |
++ if(is_rsa) { |
332 |
++ type = CKO_PUBLIC_KEY; |
333 |
++ check_rv(C_FindObjectsInit(session, attr, 2)); |
334 |
++ check_rv(C_FindObjects(session, &publickey, 1, &count)); |
335 |
++ verbose_assert(count == 1); |
336 |
++ check_rv(C_FindObjectsFinal(session)); |
337 |
+ |
338 |
+- attr[0].type = CKA_MODULUS; |
339 |
+- attr[0].pValue = NULL_PTR; |
340 |
+- attr[0].ulValueLen = 0; |
341 |
++ attr[0].type = CKA_MODULUS; |
342 |
++ attr[0].pValue = NULL_PTR; |
343 |
++ attr[0].ulValueLen = 0; |
344 |
+ |
345 |
+- attr[1].type = CKA_PUBLIC_EXPONENT; |
346 |
+- attr[1].pValue = NULL_PTR; |
347 |
+- attr[1].ulValueLen = 0; |
348 |
++ attr[1].type = CKA_PUBLIC_EXPONENT; |
349 |
++ attr[1].pValue = NULL_PTR; |
350 |
++ attr[1].ulValueLen = 0; |
351 |
+ |
352 |
+- check_rv(C_GetAttributeValue(session, publickey, attr, 2)); |
353 |
++ check_rv(C_GetAttributeValue(session, publickey, attr, 2)); |
354 |
+ |
355 |
+- verbose_assert(attr[0].ulValueLen == sig_len); |
356 |
++ verbose_assert(attr[0].ulValueLen == sig_len); |
357 |
+ |
358 |
+- mod = malloc(attr[0].ulValueLen); |
359 |
+- mod[0] = 0xde; mod[1] = 0xad; mod[2] = 0xbe; mod[3] = 0xef; |
360 |
+- exp = malloc(attr[1].ulValueLen); |
361 |
+- exp[0] = 0xde; exp[1] = 0xad; exp[2] = 0xbe; exp[3] = 0xef; |
362 |
++ mod = malloc(attr[0].ulValueLen); |
363 |
++ mod[0] = 0xde; mod[1] = 0xad; mod[2] = 0xbe; mod[3] = 0xef; |
364 |
++ exp = malloc(attr[1].ulValueLen); |
365 |
++ exp[0] = 0xde; exp[1] = 0xad; exp[2] = 0xbe; exp[3] = 0xef; |
366 |
+ |
367 |
+- attr[0].pValue = mod; |
368 |
+- attr[1].pValue = exp; |
369 |
++ attr[0].pValue = mod; |
370 |
++ attr[1].pValue = exp; |
371 |
+ |
372 |
+- check_rv(C_GetAttributeValue(session, publickey, attr, 2)); |
373 |
++ check_rv(C_GetAttributeValue(session, publickey, attr, 2)); |
374 |
+ |
375 |
+- printf("Received key modulus with length %lu:\n", attr[0].ulValueLen); |
376 |
+- hex_dump((char*)mod, attr[0].ulValueLen); |
377 |
++ printf("Received key modulus with length %lu:\n", attr[0].ulValueLen); |
378 |
++ hex_dump((char*)mod, attr[0].ulValueLen); |
379 |
+ |
380 |
+- printf("Received public exponent of key with length %lu:\n", attr[1].ulValueLen); |
381 |
+- hex_dump((char*)exp, attr[1].ulValueLen); |
382 |
++ printf("Received public exponent of key with length %lu:\n", attr[1].ulValueLen); |
383 |
++ hex_dump((char*)exp, attr[1].ulValueLen); |
384 |
++ } |
385 |
+ |
386 |
+-#if HAVE_OPENSSL |
387 |
+- return verify_sig(sig, sig_len, mod, attr[0].ulValueLen, exp, attr[1].ulValueLen); |
388 |
++#if HAVE_OPENSSL && OPENSSL_VERSION_NUMBER > 0x10100000L |
389 |
++ unsigned char cert[4096]; |
390 |
++ attr[0].type = CKA_CLASS; |
391 |
++ attr[0].pValue = &type; |
392 |
++ type = CKO_CERTIFICATE; |
393 |
++ attr[0].ulValueLen = sizeof(CK_ULONG); |
394 |
++ |
395 |
++ attr[1].type = CKA_LABEL; |
396 |
++ attr[1].pValue = label; |
397 |
++ attr[1].ulValueLen = strlen(label); |
398 |
++ |
399 |
++ check_rv(C_FindObjectsInit(session, attr, 2)); |
400 |
++ check_rv(C_FindObjects(session, &certificate, 1, &count)); |
401 |
++ verbose_assert(count == 1); |
402 |
++ check_rv(C_FindObjectsFinal(session)); |
403 |
++ |
404 |
++ attr[0].type = CKA_VALUE; |
405 |
++ attr[0].pValue = cert; |
406 |
++ attr[0].ulValueLen = sizeof(cert); |
407 |
++ |
408 |
++ check_rv(C_GetAttributeValue(session, certificate, attr, 1)); |
409 |
++ |
410 |
++ printf("Received certificate with length %lu:\n", attr[0].ulValueLen); |
411 |
++ hex_dump((char*)cert, attr[0].ulValueLen); |
412 |
++ |
413 |
++ return verify_sig(sig, sig_len, cert, attr[0].ulValueLen, is_rsa); |
414 |
+ #else |
415 |
+ return TEST_RV_OK; |
416 |
+ #endif |