opengl32: Drop the ChoosePixelFormat test as it is very unreliable.
[wine] / dlls / wldap32 / bind.c
1 /*
2  * WLDAP32 - LDAP support for Wine
3  *
4  * Copyright 2005 Hans Leidekker
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22
23 #include "wine/port.h"
24 #include "wine/debug.h"
25
26 #include <stdarg.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winnls.h"
31
32 #ifdef HAVE_LDAP_H
33 #include <ldap.h>
34 #endif
35
36 #include "winldap_private.h"
37 #include "wldap32.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(wldap32);
40
41 /***********************************************************************
42  *      ldap_bindA     (WLDAP32.@)
43  *
44  * See ldap_bindW.
45  */
46 ULONG CDECL ldap_bindA( WLDAP32_LDAP *ld, PCHAR dn, PCHAR cred, ULONG method )
47 {
48     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
49 #ifdef HAVE_LDAP
50     WCHAR *dnW = NULL, *credW = NULL;
51
52     ret = WLDAP32_LDAP_NO_MEMORY;
53
54     TRACE( "(%p, %s, %p, 0x%08x)\n", ld, debugstr_a(dn), cred, method );
55
56     if (!ld) return ~0UL;
57
58     if (dn) {
59         dnW = strAtoW( dn );
60         if (!dnW) goto exit;
61     }
62     if (cred) {
63         credW = strAtoW( cred );
64         if (!credW) goto exit;
65     }
66
67     ret = ldap_bindW( ld, dnW, credW, method );
68
69 exit:
70     strfreeW( dnW );
71     strfreeW( credW );
72
73 #endif
74     return ret;
75 }
76
77 /***********************************************************************
78  *      ldap_bindW     (WLDAP32.@)
79  *
80  * Authenticate with an LDAP server (asynchronous operation).
81  *
82  * PARAMS
83  *  ld      [I] Pointer to an LDAP context.
84  *  dn      [I] DN of entry to bind as.
85  *  cred    [I] Credentials (e.g. password string).
86  *  method  [I] Authentication method.
87  *
88  * RETURNS
89  *  Success: Message ID of the bind operation.
90  *  Failure: An LDAP error code.
91  *
92  * NOTES
93  *  Only LDAP_AUTH_SIMPLE is supported (just like native).
94  */
95 ULONG CDECL ldap_bindW( WLDAP32_LDAP *ld, PWCHAR dn, PWCHAR cred, ULONG method )
96 {
97     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
98 #ifdef HAVE_LDAP
99     char *dnU = NULL, *credU = NULL;
100     struct berval pwd = { 0, NULL };
101     int msg;
102
103     ret = WLDAP32_LDAP_NO_MEMORY;
104
105     TRACE( "(%p, %s, %p, 0x%08x)\n", ld, debugstr_w(dn), cred, method );
106
107     if (!ld) return ~0UL;
108     if (method != LDAP_AUTH_SIMPLE) return WLDAP32_LDAP_PARAM_ERROR;
109
110     if (dn) {
111         dnU = strWtoU( dn );
112         if (!dnU) goto exit;
113     }
114     if (cred) {
115         credU = strWtoU( cred );
116         if (!credU) goto exit;
117
118         pwd.bv_len = strlen( credU );
119         pwd.bv_val = credU;
120     }
121
122     ret = ldap_sasl_bind( ld, dnU, LDAP_SASL_SIMPLE, &pwd, NULL, NULL, &msg );
123
124     if (ret == LDAP_SUCCESS)
125         ret = msg;
126     else
127         ret = ~0UL;
128
129 exit:
130     strfreeU( dnU );
131     strfreeU( credU );
132
133 #endif
134     return ret;
135 }
136
137 /***********************************************************************
138  *      ldap_bind_sA     (WLDAP32.@)
139  *
140  * See ldap_bind_sW.
141  */
142 ULONG CDECL ldap_bind_sA( WLDAP32_LDAP *ld, PCHAR dn, PCHAR cred, ULONG method )
143 {
144     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
145 #ifdef HAVE_LDAP
146     WCHAR *dnW = NULL, *credW = NULL;
147
148     ret = WLDAP32_LDAP_NO_MEMORY;
149
150     TRACE( "(%p, %s, %p, 0x%08x)\n", ld, debugstr_a(dn), cred, method );
151
152     if (!ld) return WLDAP32_LDAP_PARAM_ERROR;
153
154     if (dn) {
155         dnW = strAtoW( dn );
156         if (!dnW) goto exit;
157     }
158     if (cred) {
159         credW = strAtoW( cred );
160         if (!credW) goto exit;
161     }
162
163     ret = ldap_bind_sW( ld, dnW, credW, method );
164
165 exit:
166     strfreeW( dnW );
167     strfreeW( credW );
168
169 #endif
170     return ret;
171 }
172
173 /***********************************************************************
174  *      ldap_bind_sW     (WLDAP32.@)
175  *
176  * Authenticate with an LDAP server (synchronous operation).
177  *
178  * PARAMS
179  *  ld      [I] Pointer to an LDAP context.
180  *  dn      [I] DN of entry to bind as.
181  *  cred    [I] Credentials (e.g. password string).
182  *  method  [I] Authentication method.
183  *
184  * RETURNS
185  *  Success: LDAP_SUCCESS
186  *  Failure: An LDAP error code.
187  */
188 ULONG CDECL ldap_bind_sW( WLDAP32_LDAP *ld, PWCHAR dn, PWCHAR cred, ULONG method )
189 {
190     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
191 #ifdef HAVE_LDAP
192     char *dnU = NULL, *credU = NULL;
193     struct berval pwd = { 0, NULL };
194
195     ret = WLDAP32_LDAP_NO_MEMORY;
196
197     TRACE( "(%p, %s, %p, 0x%08x)\n", ld, debugstr_w(dn), cred, method );
198
199     if (!ld) return WLDAP32_LDAP_PARAM_ERROR;
200     if (method != LDAP_AUTH_SIMPLE) return WLDAP32_LDAP_PARAM_ERROR;
201
202     if (dn) {
203         dnU = strWtoU( dn );
204         if (!dnU) goto exit;
205     }
206     if (cred) {
207         credU = strWtoU( cred );
208         if (!credU) goto exit;
209
210         pwd.bv_len = strlen( credU );
211         pwd.bv_val = credU;
212     }
213
214     ret = ldap_sasl_bind_s( ld, dnU, LDAP_SASL_SIMPLE, &pwd, NULL, NULL, NULL );
215
216 exit:
217     strfreeU( dnU );
218     strfreeU( credU );
219
220 #endif
221     return ret;
222 }
223
224 /***********************************************************************
225  *      ldap_sasl_bindA     (WLDAP32.@)
226  *
227  * See ldap_sasl_bindW.
228  */
229 ULONG CDECL ldap_sasl_bindA( WLDAP32_LDAP *ld, const PCHAR dn,
230     const PCHAR mechanism, const BERVAL *cred, PLDAPControlA *serverctrls,
231     PLDAPControlA *clientctrls, int *message )
232 {
233     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
234 #ifdef HAVE_LDAP
235     WCHAR *dnW, *mechanismW = NULL;
236     LDAPControlW **serverctrlsW = NULL, **clientctrlsW = NULL;
237
238     ret = WLDAP32_LDAP_NO_MEMORY;
239
240     TRACE( "(%p, %s, %s, %p, %p, %p, %p)\n", ld, debugstr_a(dn),
241            debugstr_a(mechanism), cred, serverctrls, clientctrls, message );
242
243     if (!ld || !dn || !mechanism || !cred || !message)
244         return WLDAP32_LDAP_PARAM_ERROR;
245
246     dnW = strAtoW( dn );
247     if (!dnW) goto exit;
248
249     mechanismW = strAtoW( mechanism );
250     if (!mechanismW) goto exit;
251
252     if (serverctrls) {
253         serverctrlsW = controlarrayAtoW( serverctrls );
254         if (!serverctrlsW) goto exit;
255     }
256     if (clientctrls) {
257         clientctrlsW = controlarrayAtoW( clientctrls );
258         if (!clientctrlsW) goto exit;
259     }
260
261     ret = ldap_sasl_bindW( ld, dnW, mechanismW, cred, serverctrlsW, clientctrlsW, message );
262
263 exit:
264     strfreeW( dnW );
265     strfreeW( mechanismW );
266     controlarrayfreeW( serverctrlsW );
267     controlarrayfreeW( clientctrlsW );
268
269 #endif
270     return ret;
271 }
272
273 /***********************************************************************
274  *      ldap_sasl_bindW     (WLDAP32.@)
275  *
276  * Authenticate with an LDAP server using SASL (asynchronous operation).
277  *
278  * PARAMS
279  *  ld          [I] Pointer to an LDAP context.
280  *  dn          [I] DN of entry to bind as.
281  *  mechanism   [I] Authentication method.
282  *  cred        [I] Credentials.
283  *  serverctrls [I] Array of LDAP server controls.
284  *  clientctrls [I] Array of LDAP client controls.
285  *  message     [O] Message ID of the bind operation. 
286  *
287  * RETURNS
288  *  Success: LDAP_SUCCESS
289  *  Failure: An LDAP error code.
290  *
291  * NOTES
292  *  The serverctrls and clientctrls parameters are optional and should
293  *  be set to NULL if not used.
294  */
295 ULONG CDECL ldap_sasl_bindW( WLDAP32_LDAP *ld, const PWCHAR dn,
296     const PWCHAR mechanism, const BERVAL *cred, PLDAPControlW *serverctrls,
297     PLDAPControlW *clientctrls, int *message )
298 {
299     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
300 #ifdef HAVE_LDAP
301     char *dnU, *mechanismU = NULL;
302     LDAPControl **serverctrlsU = NULL, **clientctrlsU = NULL;
303     struct berval credU;
304
305     ret = WLDAP32_LDAP_NO_MEMORY;
306
307     TRACE( "(%p, %s, %s, %p, %p, %p, %p)\n", ld, debugstr_w(dn),
308            debugstr_w(mechanism), cred, serverctrls, clientctrls, message );
309
310     if (!ld || !dn || !mechanism || !cred || !message)
311         return WLDAP32_LDAP_PARAM_ERROR;
312
313     dnU = strWtoU( dn );
314     if (!dnU) goto exit;
315
316     mechanismU = strWtoU( mechanism );
317     if (!mechanismU) goto exit;
318
319     if (serverctrls) {
320         serverctrlsU = controlarrayWtoU( serverctrls );
321         if (!serverctrlsU) goto exit;
322     }
323     if (clientctrls) {
324         clientctrlsU = controlarrayWtoU( clientctrls );
325         if (!clientctrlsU) goto exit;
326     }
327
328     credU.bv_len = cred->bv_len;
329     credU.bv_val = cred->bv_val;
330
331     ret = ldap_sasl_bind( ld, dnU, mechanismU, &credU,
332                           serverctrlsU, clientctrlsU, message );
333
334 exit:
335     strfreeU( dnU );
336     strfreeU( mechanismU );
337     controlarrayfreeU( serverctrlsU );
338     controlarrayfreeU( clientctrlsU );
339
340 #endif
341     return ret;
342 }
343
344 /***********************************************************************
345  *      ldap_sasl_bind_sA     (WLDAP32.@)
346  *
347  * See ldap_sasl_bind_sW.
348  */
349 ULONG CDECL ldap_sasl_bind_sA( WLDAP32_LDAP *ld, const PCHAR dn,
350     const PCHAR mechanism, const BERVAL *cred, PLDAPControlA *serverctrls,
351     PLDAPControlA *clientctrls, PBERVAL *serverdata )
352 {
353     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
354 #ifdef HAVE_LDAP
355     WCHAR *dnW, *mechanismW = NULL;
356     LDAPControlW **serverctrlsW = NULL, **clientctrlsW = NULL;
357
358     ret = WLDAP32_LDAP_NO_MEMORY;
359
360     TRACE( "(%p, %s, %s, %p, %p, %p, %p)\n", ld, debugstr_a(dn),
361            debugstr_a(mechanism), cred, serverctrls, clientctrls, serverdata );
362
363     if (!ld || !dn || !mechanism || !cred || !serverdata)
364         return WLDAP32_LDAP_PARAM_ERROR;
365
366     dnW = strAtoW( dn );
367     if (!dnW) goto exit;
368
369     mechanismW = strAtoW( mechanism );
370     if (!mechanismW) goto exit;
371
372     if (serverctrls) {
373         serverctrlsW = controlarrayAtoW( serverctrls );
374         if (!serverctrlsW) goto exit;
375     }
376     if (clientctrls) {
377         clientctrlsW = controlarrayAtoW( clientctrls );
378         if (!clientctrlsW) goto exit;
379     }
380
381     ret = ldap_sasl_bind_sW( ld, dnW, mechanismW, cred, serverctrlsW, clientctrlsW, serverdata );
382
383 exit:
384     strfreeW( dnW );
385     strfreeW( mechanismW );
386     controlarrayfreeW( serverctrlsW );
387     controlarrayfreeW( clientctrlsW );
388
389 #endif
390     return ret;
391 }
392
393 /***********************************************************************
394  *      ldap_sasl_bind_sW     (WLDAP32.@)
395  *
396  * Authenticate with an LDAP server using SASL (synchronous operation).
397  *
398  * PARAMS
399  *  ld          [I] Pointer to an LDAP context.
400  *  dn          [I] DN of entry to bind as.
401  *  mechanism   [I] Authentication method.
402  *  cred        [I] Credentials.
403  *  serverctrls [I] Array of LDAP server controls.
404  *  clientctrls [I] Array of LDAP client controls.
405  *  serverdata  [O] Authentication response from the server.
406  *
407  * RETURNS
408  *  Success: LDAP_SUCCESS
409  *  Failure: An LDAP error code.
410  *
411  * NOTES
412  *  The serverctrls and clientctrls parameters are optional and should
413  *  be set to NULL if not used.
414  */
415 ULONG CDECL ldap_sasl_bind_sW( WLDAP32_LDAP *ld, const PWCHAR dn,
416     const PWCHAR mechanism, const BERVAL *cred, PLDAPControlW *serverctrls,
417     PLDAPControlW *clientctrls, PBERVAL *serverdata )
418 {
419     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
420 #ifdef HAVE_LDAP
421     char *dnU, *mechanismU = NULL;
422     LDAPControl **serverctrlsU = NULL, **clientctrlsU = NULL;
423     struct berval credU;
424
425     ret = WLDAP32_LDAP_NO_MEMORY;
426
427     TRACE( "(%p, %s, %s, %p, %p, %p, %p)\n", ld, debugstr_w(dn),
428            debugstr_w(mechanism), cred, serverctrls, clientctrls, serverdata );
429
430     if (!ld || !dn || !mechanism || !cred || !serverdata)
431         return WLDAP32_LDAP_PARAM_ERROR;
432
433     dnU = strWtoU( dn );
434     if (!dnU) goto exit;
435
436     mechanismU = strWtoU( mechanism );
437     if (!mechanismU) goto exit;
438
439     if (serverctrls) {
440         serverctrlsU = controlarrayWtoU( serverctrls );
441         if (!serverctrlsU) goto exit;
442     }
443     if (clientctrls) {
444         clientctrlsU = controlarrayWtoU( clientctrls );
445         if (!clientctrlsU) goto exit;
446     }
447
448     credU.bv_len = cred->bv_len;
449     credU.bv_val = cred->bv_val;
450
451     ret = ldap_sasl_bind_s( ld, dnU, mechanismU, &credU,
452                             serverctrlsU, clientctrlsU, (struct berval **)serverdata );
453
454 exit:
455     strfreeU( dnU );
456     strfreeU( mechanismU );
457     controlarrayfreeU( serverctrlsU );
458     controlarrayfreeU( clientctrlsU );
459
460 #endif
461     return ret;
462 }
463
464 /***********************************************************************
465  *      ldap_simple_bindA     (WLDAP32.@)
466  *
467  * See ldap_simple_bindW.
468  */
469 ULONG CDECL ldap_simple_bindA( WLDAP32_LDAP *ld, PCHAR dn, PCHAR passwd )
470 {
471     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
472 #ifdef HAVE_LDAP
473     WCHAR *dnW = NULL, *passwdW = NULL;
474
475     ret = WLDAP32_LDAP_NO_MEMORY;
476
477     TRACE( "(%p, %s, %p)\n", ld, debugstr_a(dn), passwd );
478
479     if (!ld) return ~0UL;
480
481     if (dn) {
482         dnW = strAtoW( dn );
483         if (!dnW) goto exit;
484     }
485     if (passwd) {
486         passwdW = strAtoW( passwd );
487         if (!passwdW) goto exit;
488     }
489
490     ret = ldap_simple_bindW( ld, dnW, passwdW );
491
492 exit:
493     strfreeW( dnW );
494     strfreeW( passwdW );
495
496 #endif
497     return ret;
498 }
499
500 /***********************************************************************
501  *      ldap_simple_bindW     (WLDAP32.@)
502  *
503  * Authenticate with an LDAP server (asynchronous operation).
504  *
505  * PARAMS
506  *  ld      [I] Pointer to an LDAP context.
507  *  dn      [I] DN of entry to bind as.
508  *  passwd  [I] Password string.
509  *
510  * RETURNS
511  *  Success: Message ID of the bind operation.
512  *  Failure: An LDAP error code.
513  *
514  * NOTES
515  *  Set dn and passwd to NULL to bind as an anonymous user. 
516  */
517 ULONG CDECL ldap_simple_bindW( WLDAP32_LDAP *ld, PWCHAR dn, PWCHAR passwd )
518 {
519     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
520 #ifdef HAVE_LDAP
521     char *dnU = NULL, *passwdU = NULL;
522     struct berval pwd = { 0, NULL };
523     int msg;
524
525     ret = WLDAP32_LDAP_NO_MEMORY;
526
527     TRACE( "(%p, %s, %p)\n", ld, debugstr_w(dn), passwd );
528
529     if (!ld) return ~0UL;
530
531     if (dn) {
532         dnU = strWtoU( dn );
533         if (!dnU) goto exit;
534     }
535     if (passwd) {
536         passwdU = strWtoU( passwd );
537         if (!passwdU) goto exit;
538
539         pwd.bv_len = strlen( passwdU );
540         pwd.bv_val = passwdU;
541     }
542
543     ret = ldap_sasl_bind( ld, dnU, LDAP_SASL_SIMPLE, &pwd, NULL, NULL, &msg );
544
545     if (ret == LDAP_SUCCESS)
546         ret = msg;
547     else
548         ret = ~0UL;
549
550 exit:
551     strfreeU( dnU );
552     strfreeU( passwdU );
553
554 #endif
555     return ret;
556 }
557
558 /***********************************************************************
559  *      ldap_simple_bind_sA     (WLDAP32.@)
560  *
561  * See ldap_simple_bind_sW.
562  */
563 ULONG CDECL ldap_simple_bind_sA( WLDAP32_LDAP *ld, PCHAR dn, PCHAR passwd )
564 {
565     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
566 #ifdef HAVE_LDAP
567     WCHAR *dnW = NULL, *passwdW = NULL;
568
569     ret = WLDAP32_LDAP_NO_MEMORY;
570
571     TRACE( "(%p, %s, %p)\n", ld, debugstr_a(dn), passwd );
572
573     if (!ld) return WLDAP32_LDAP_PARAM_ERROR;
574
575     if (dn) {
576         dnW = strAtoW( dn );
577         if (!dnW) goto exit;
578     }
579     if (passwd) {
580         passwdW = strAtoW( passwd );
581         if (!passwdW) goto exit;
582     }
583
584     ret = ldap_simple_bind_sW( ld, dnW, passwdW );
585
586 exit:
587     strfreeW( dnW );
588     strfreeW( passwdW );
589
590 #endif
591     return ret;
592 }
593
594 /***********************************************************************
595  *      ldap_simple_bind_sW     (WLDAP32.@)
596  *
597  * Authenticate with an LDAP server (synchronous operation).
598  *
599  * PARAMS
600  *  ld      [I] Pointer to an LDAP context.
601  *  dn      [I] DN of entry to bind as.
602  *  passwd  [I] Password string.
603  *
604  * RETURNS
605  *  Success: LDAP_SUCCESS
606  *  Failure: An LDAP error code.
607  *
608  * NOTES
609  *  Set dn and passwd to NULL to bind as an anonymous user. 
610  */
611 ULONG CDECL ldap_simple_bind_sW( WLDAP32_LDAP *ld, PWCHAR dn, PWCHAR passwd )
612 {
613     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
614 #ifdef HAVE_LDAP
615     char *dnU = NULL, *passwdU = NULL;
616     struct berval pwd = { 0, NULL };
617
618     ret = WLDAP32_LDAP_NO_MEMORY;
619
620     TRACE( "(%p, %s, %p)\n", ld, debugstr_w(dn), passwd );
621
622     if (!ld) return WLDAP32_LDAP_PARAM_ERROR;
623
624     if (dn) {
625         dnU = strWtoU( dn );
626         if (!dnU) goto exit;
627     }
628     if (passwd) {
629         passwdU = strWtoU( passwd );
630         if (!passwdU) goto exit;
631
632         pwd.bv_len = strlen( passwdU );
633         pwd.bv_val = passwdU;
634     }
635
636     ret = ldap_sasl_bind_s( ld, dnU, LDAP_SASL_SIMPLE, &pwd, NULL, NULL, NULL );
637
638 exit:
639     strfreeU( dnU );
640     strfreeU( passwdU );
641
642 #endif
643     return ret;
644 }
645
646 /***********************************************************************
647  *      ldap_unbind     (WLDAP32.@)
648  *
649  * Close LDAP connection and free resources (asynchronous operation).
650  *
651  * PARAMS
652  *  ld  [I] Pointer to an LDAP context.
653  *
654  * RETURNS
655  *  Success: LDAP_SUCCESS
656  *  Failure: An LDAP error code.
657  */
658 ULONG CDECL WLDAP32_ldap_unbind( WLDAP32_LDAP *ld )
659 {
660     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
661 #ifdef HAVE_LDAP
662
663     TRACE( "(%p)\n", ld );
664
665     if (ld)
666         ret = ldap_unbind_ext( ld, NULL, NULL );
667     else
668         ret = WLDAP32_LDAP_PARAM_ERROR;
669
670 #endif
671     return ret;
672 }
673
674 /***********************************************************************
675  *      ldap_unbind_s     (WLDAP32.@)
676  *
677  * Close LDAP connection and free resources (synchronous operation).
678  *
679  * PARAMS
680  *  ld  [I] Pointer to an LDAP context.
681  *
682  * RETURNS
683  *  Success: LDAP_SUCCESS
684  *  Failure: An LDAP error code.
685  */
686 ULONG CDECL WLDAP32_ldap_unbind_s( WLDAP32_LDAP *ld )
687 {
688     ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
689 #ifdef HAVE_LDAP
690
691     TRACE( "(%p)\n", ld );
692
693     if (ld)
694         ret = ldap_unbind_ext_s( ld, NULL, NULL );
695     else
696         ret = WLDAP32_LDAP_PARAM_ERROR;
697
698 #endif
699     return ret;
700 }