* Slap *some* sense into our header inclusions.
[reactos.git] / reactos / dll / win32 / wldap32 / init.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 #ifdef HAVE_LDAP
40 /* Should eventually be determined by the algorithm documented on MSDN. */
41 static const WCHAR defaulthost[] = { 'l','o','c','a','l','h','o','s','t',0 };
42
43 /* Split a space separated string of hostnames into a string array */
44 static char **split_hostnames( const char *hostnames )
45 {
46 char **res, *str, *p, *q;
47 unsigned int i = 0;
48
49 str = strdupU( hostnames );
50 if (!str) return NULL;
51
52 p = str;
53 while (isspace( *p )) p++;
54 if (*p) i++;
55
56 while (*p)
57 {
58 if (isspace( *p ))
59 {
60 while (isspace( *p )) p++;
61 if (*p) i++;
62 }
63 p++;
64 }
65
66 res = HeapAlloc( GetProcessHeap(), 0, (i + 1) * sizeof(char *) );
67 if (!res)
68 {
69 HeapFree( GetProcessHeap(), 0, str );
70 return NULL;
71 }
72
73 p = str;
74 while (isspace( *p )) p++;
75
76 q = p;
77 i = 0;
78
79 while (*p)
80 {
81 if (p[1] != '\0')
82 {
83 if (isspace( *p ))
84 {
85 *p = '\0'; p++;
86 res[i] = strdupU( q );
87 if (!res[i]) goto oom;
88 i++;
89
90 while (isspace( *p )) p++;
91 q = p;
92 }
93 }
94 else
95 {
96 res[i] = strdupU( q );
97 if (!res[i]) goto oom;
98 i++;
99 }
100 p++;
101 }
102 res[i] = NULL;
103
104 HeapFree( GetProcessHeap(), 0, str );
105 return res;
106
107 oom:
108 while (i > 0) strfreeU( res[--i] );
109
110 HeapFree( GetProcessHeap(), 0, res );
111 HeapFree( GetProcessHeap(), 0, str );
112
113 return NULL;
114 }
115
116 /* Determine if a URL starts with a known LDAP scheme */
117 static int has_ldap_scheme( char *url )
118 {
119 if (!strncasecmp( url, "ldap://", 7 ) ||
120 !strncasecmp( url, "ldaps://", 8 ) ||
121 !strncasecmp( url, "ldapi://", 8 ) ||
122 !strncasecmp( url, "cldap://", 8 )) return 1;
123 return 0;
124 }
125
126 /* Flatten an array of hostnames into a space separated string of URLs.
127 * Prepend a given scheme and append a given portnumber to each hostname
128 * if necessary.
129 */
130 static char *join_hostnames( const char *scheme, char **hostnames, ULONG portnumber )
131 {
132 char *res, *p, *q, **v;
133 unsigned int i = 0, size = 0;
134 static const char sep[] = " ", fmt[] = ":%d";
135 char port[7];
136
137 sprintf( port, fmt, portnumber );
138
139 for (v = hostnames; *v; v++)
140 {
141 if (!has_ldap_scheme( *v ))
142 {
143 size += strlen( scheme );
144 q = *v;
145 }
146 else
147 /* skip past colon in scheme prefix */
148 q = strchr( *v, '/' );
149
150 size += strlen( *v );
151
152 if (!strchr( q, ':' ))
153 size += strlen( port );
154
155 i++;
156 }
157
158 size += (i - 1) * strlen( sep );
159
160 res = HeapAlloc( GetProcessHeap(), 0, size + 1 );
161 if (!res) return NULL;
162
163 p = res;
164 for (v = hostnames; *v; v++)
165 {
166 if (v != hostnames)
167 {
168 strcpy( p, sep );
169 p += strlen( sep );
170 }
171
172 if (!has_ldap_scheme( *v ))
173 {
174 strcpy( p, scheme );
175 p += strlen( scheme );
176 q = *v;
177 }
178 else
179 /* skip past colon in scheme prefix */
180 q = strchr( *v, '/' );
181
182 strcpy( p, *v );
183 p += strlen( *v );
184
185 if (!strchr( q, ':' ))
186 {
187 strcpy( p, port );
188 p += strlen( port );
189 }
190 }
191 return res;
192 }
193
194 static char *urlify_hostnames( const char *scheme, char *hostnames, ULONG port )
195 {
196 char *url = NULL, **strarray;
197
198 strarray = split_hostnames( hostnames );
199 if (strarray)
200 url = join_hostnames( scheme, strarray, port );
201 else
202 return NULL;
203
204 strarrayfreeU( strarray );
205 return url;
206 }
207 #endif
208
209 WINE_DEFAULT_DEBUG_CHANNEL(wldap32);
210
211 /***********************************************************************
212 * cldap_openA (WLDAP32.@)
213 *
214 * See cldap_openW.
215 */
216 WLDAP32_LDAP * CDECL cldap_openA( PCHAR hostname, ULONG portnumber )
217 {
218 #ifdef HAVE_LDAP
219 WLDAP32_LDAP *ld = NULL;
220 WCHAR *hostnameW = NULL;
221
222 TRACE( "(%s, %d)\n", debugstr_a(hostname), portnumber );
223
224 if (hostname) {
225 hostnameW = strAtoW( hostname );
226 if (!hostnameW) goto exit;
227 }
228
229 ld = cldap_openW( hostnameW, portnumber );
230
231 exit:
232 strfreeW( hostnameW );
233 return ld;
234
235 #else
236 return NULL;
237 #endif
238 }
239
240 /***********************************************************************
241 * cldap_openW (WLDAP32.@)
242 *
243 * Initialize an LDAP context and create a UDP connection.
244 *
245 * PARAMS
246 * hostname [I] Name of the host to connect to.
247 * portnumber [I] Portnumber to use.
248 *
249 * RETURNS
250 * Success: Pointer to an LDAP context.
251 * Failure: NULL
252 *
253 * NOTES
254 * The hostname string can be a space separated string of hostnames,
255 * in which case the LDAP runtime will try to connect to the hosts
256 * in order, until a connection can be made. A hostname may have a
257 * trailing portnumber (separated from the hostname by a ':'), which
258 * will take precedence over the portnumber supplied as a parameter
259 * to this function.
260 */
261 WLDAP32_LDAP * CDECL cldap_openW( PWCHAR hostname, ULONG portnumber )
262 {
263 #ifdef HAVE_LDAP
264 LDAP *ld = NULL;
265 char *hostnameU = NULL, *url = NULL;
266
267 TRACE( "(%s, %d)\n", debugstr_w(hostname), portnumber );
268
269 if (hostname) {
270 hostnameU = strWtoU( hostname );
271 if (!hostnameU) goto exit;
272 }
273 else {
274 hostnameU = strWtoU( defaulthost );
275 if (!hostnameU) goto exit;
276 }
277
278 url = urlify_hostnames( "cldap://", hostnameU, portnumber );
279 if (!url) goto exit;
280
281 ldap_initialize( &ld, url );
282
283 exit:
284 strfreeU( hostnameU );
285 strfreeU( url );
286 return ld;
287
288 #else
289 return NULL;
290 #endif
291 }
292
293 /***********************************************************************
294 * ldap_connect (WLDAP32.@)
295 *
296 * Connect to an LDAP server.
297 *
298 * PARAMS
299 * ld [I] Pointer to an LDAP context.
300 * timeout [I] Pointer to an l_timeval structure specifying the
301 * timeout in seconds.
302 *
303 * RETURNS
304 * Success: LDAP_SUCCESS
305 * Failure: An LDAP error code.
306 *
307 * NOTES
308 * The timeout parameter may be NULL in which case a default timeout
309 * value will be used.
310 */
311 ULONG CDECL ldap_connect( WLDAP32_LDAP *ld, struct l_timeval *timeout )
312 {
313 TRACE( "(%p, %p)\n", ld, timeout );
314
315 if (!ld) return WLDAP32_LDAP_PARAM_ERROR;
316 return WLDAP32_LDAP_SUCCESS; /* FIXME: do something, e.g. ping the host */
317 }
318
319 /***********************************************************************
320 * ldap_initA (WLDAP32.@)
321 *
322 * See ldap_initW.
323 */
324 WLDAP32_LDAP * CDECL ldap_initA( PCHAR hostname, ULONG portnumber )
325 {
326 #ifdef HAVE_LDAP
327 WLDAP32_LDAP *ld = NULL;
328 WCHAR *hostnameW = NULL;
329
330 TRACE( "(%s, %d)\n", debugstr_a(hostname), portnumber );
331
332 if (hostname) {
333 hostnameW = strAtoW( hostname );
334 if (!hostnameW) goto exit;
335 }
336
337 ld = ldap_initW( hostnameW, portnumber );
338
339 exit:
340 strfreeW( hostnameW );
341 return ld;
342
343 #else
344 return NULL;
345 #endif
346 }
347
348 /***********************************************************************
349 * ldap_initW (WLDAP32.@)
350 *
351 * Initialize an LDAP context and create a TCP connection.
352 *
353 * PARAMS
354 * hostname [I] Name of the host to connect to.
355 * portnumber [I] Portnumber to use.
356 *
357 * RETURNS
358 * Success: Pointer to an LDAP context.
359 * Failure: NULL
360 *
361 * NOTES
362 * The hostname string can be a space separated string of hostnames,
363 * in which case the LDAP runtime will try to connect to the hosts
364 * in order, until a connection can be made. A hostname may have a
365 * trailing portnumber (separated from the hostname by a ':'), which
366 * will take precedence over the portnumber supplied as a parameter
367 * to this function. The connection will not be made until the first
368 * LDAP function that needs it is called.
369 */
370 WLDAP32_LDAP * CDECL ldap_initW( PWCHAR hostname, ULONG portnumber )
371 {
372 #ifdef HAVE_LDAP
373 LDAP *ld = NULL;
374 char *hostnameU = NULL, *url = NULL;
375
376 TRACE( "(%s, %d)\n", debugstr_w(hostname), portnumber );
377
378 if (hostname) {
379 hostnameU = strWtoU( hostname );
380 if (!hostnameU) goto exit;
381 }
382 else {
383 hostnameU = strWtoU( defaulthost );
384 if (!hostnameU) goto exit;
385 }
386
387 url = urlify_hostnames( "ldap://", hostnameU, portnumber );
388 if (!url) goto exit;
389
390 ldap_initialize( &ld, url );
391
392 exit:
393 strfreeU( hostnameU );
394 strfreeU( url );
395 return ld;
396
397 #else
398 return NULL;
399 #endif
400 }
401
402 /***********************************************************************
403 * ldap_openA (WLDAP32.@)
404 *
405 * See ldap_openW.
406 */
407 WLDAP32_LDAP * CDECL ldap_openA( PCHAR hostname, ULONG portnumber )
408 {
409 #ifdef HAVE_LDAP
410 WLDAP32_LDAP *ld = NULL;
411 WCHAR *hostnameW = NULL;
412
413 TRACE( "(%s, %d)\n", debugstr_a(hostname), portnumber );
414
415 if (hostname) {
416 hostnameW = strAtoW( hostname );
417 if (!hostnameW) goto exit;
418 }
419
420 ld = ldap_openW( hostnameW, portnumber );
421
422 exit:
423 strfreeW( hostnameW );
424 return ld;
425
426 #else
427 return NULL;
428 #endif
429 }
430
431 /***********************************************************************
432 * ldap_openW (WLDAP32.@)
433 *
434 * Initialize an LDAP context and create a TCP connection.
435 *
436 * PARAMS
437 * hostname [I] Name of the host to connect to.
438 * portnumber [I] Portnumber to use.
439 *
440 * RETURNS
441 * Success: Pointer to an LDAP context.
442 * Failure: NULL
443 *
444 * NOTES
445 * The hostname string can be a space separated string of hostnames,
446 * in which case the LDAP runtime will try to connect to the hosts
447 * in order, until a connection can be made. A hostname may have a
448 * trailing portnumber (separated from the hostname by a ':'), which
449 * will take precedence over the portnumber supplied as a parameter
450 * to this function.
451 */
452 WLDAP32_LDAP * CDECL ldap_openW( PWCHAR hostname, ULONG portnumber )
453 {
454 #ifdef HAVE_LDAP
455 LDAP *ld = NULL;
456 char *hostnameU = NULL, *url = NULL;
457
458 TRACE( "(%s, %d)\n", debugstr_w(hostname), portnumber );
459
460 if (hostname) {
461 hostnameU = strWtoU( hostname );
462 if (!hostnameU) goto exit;
463 }
464 else {
465 hostnameU = strWtoU( defaulthost );
466 if (!hostnameU) goto exit;
467 }
468
469 url = urlify_hostnames( "ldap://", hostnameU, portnumber );
470 if (!url) goto exit;
471
472 ldap_initialize( &ld, url );
473
474 exit:
475 strfreeU( hostnameU );
476 strfreeU( url );
477 return ld;
478
479 #else
480 return NULL;
481 #endif
482 }
483
484 /***********************************************************************
485 * ldap_sslinitA (WLDAP32.@)
486 *
487 * See ldap_sslinitW.
488 */
489 WLDAP32_LDAP * CDECL ldap_sslinitA( PCHAR hostname, ULONG portnumber, int secure )
490 {
491 #ifdef HAVE_LDAP
492 WLDAP32_LDAP *ld;
493 WCHAR *hostnameW = NULL;
494
495 TRACE( "(%s, %d, 0x%08x)\n", debugstr_a(hostname), portnumber, secure );
496
497 if (hostname) {
498 hostnameW = strAtoW( hostname );
499 if (!hostnameW) return NULL;
500 }
501
502 ld = ldap_sslinitW( hostnameW, portnumber, secure );
503
504 strfreeW( hostnameW );
505 return ld;
506
507 #else
508 return NULL;
509 #endif
510 }
511
512 /***********************************************************************
513 * ldap_sslinitW (WLDAP32.@)
514 *
515 * Initialize an LDAP context and create a secure TCP connection.
516 *
517 * PARAMS
518 * hostname [I] Name of the host to connect to.
519 * portnumber [I] Portnumber to use.
520 * secure [I] Ask the server to create an SSL connection.
521 *
522 * RETURNS
523 * Success: Pointer to an LDAP context.
524 * Failure: NULL
525 *
526 * NOTES
527 * The hostname string can be a space separated string of hostnames,
528 * in which case the LDAP runtime will try to connect to the hosts
529 * in order, until a connection can be made. A hostname may have a
530 * trailing portnumber (separated from the hostname by a ':'), which
531 * will take precedence over the portnumber supplied as a parameter
532 * to this function. The connection will not be made until the first
533 * LDAP function that needs it is called.
534 */
535 WLDAP32_LDAP * CDECL ldap_sslinitW( PWCHAR hostname, ULONG portnumber, int secure )
536 {
537 #ifdef HAVE_LDAP
538 WLDAP32_LDAP *ld = NULL;
539 char *hostnameU = NULL, *url = NULL;
540
541 TRACE( "(%s, %d, 0x%08x)\n", debugstr_w(hostname), portnumber, secure );
542
543 if (hostname) {
544 hostnameU = strWtoU( hostname );
545 if (!hostnameU) goto exit;
546 }
547 else {
548 hostnameU = strWtoU( defaulthost );
549 if (!hostnameU) goto exit;
550 }
551
552 if (secure)
553 url = urlify_hostnames( "ldaps://", hostnameU, portnumber );
554 else
555 url = urlify_hostnames( "ldap://", hostnameU, portnumber );
556
557 if (!url) goto exit;
558 ldap_initialize( &ld, url );
559
560 exit:
561 strfreeU( hostnameU );
562 strfreeU( url );
563 return ld;
564
565 #else
566 return NULL;
567 #endif
568 }
569
570 /***********************************************************************
571 * ldap_start_tls_sA (WLDAP32.@)
572 *
573 * See ldap_start_tls_sW.
574 */
575 ULONG CDECL ldap_start_tls_sA( WLDAP32_LDAP *ld, PULONG retval, WLDAP32_LDAPMessage **result,
576 PLDAPControlA *serverctrls, PLDAPControlA *clientctrls )
577 {
578 ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
579 #ifdef HAVE_LDAP
580 LDAPControlW **serverctrlsW = NULL, **clientctrlsW = NULL;
581
582 ret = WLDAP32_LDAP_NO_MEMORY;
583
584 TRACE( "(%p, %p, %p, %p, %p)\n", ld, retval, result, serverctrls, clientctrls );
585
586 if (!ld) return ~0u;
587
588 if (serverctrls) {
589 serverctrlsW = controlarrayAtoW( serverctrls );
590 if (!serverctrlsW) goto exit;
591 }
592 if (clientctrls) {
593 clientctrlsW = controlarrayAtoW( clientctrls );
594 if (!clientctrlsW) goto exit;
595 }
596
597 ret = ldap_start_tls_sW( ld, retval, result, serverctrlsW, clientctrlsW );
598
599 exit:
600 controlarrayfreeW( serverctrlsW );
601 controlarrayfreeW( clientctrlsW );
602
603 #endif
604 return ret;
605 }
606
607 /***********************************************************************
608 * ldap_start_tls_s (WLDAP32.@)
609 *
610 * Start TLS encryption on an LDAP connection.
611 *
612 * PARAMS
613 * ld [I] Pointer to an LDAP context.
614 * retval [I] Return value from the server.
615 * result [I] Response message from the server.
616 * serverctrls [I] Array of LDAP server controls.
617 * clientctrls [I] Array of LDAP client controls.
618 *
619 * RETURNS
620 * Success: LDAP_SUCCESS
621 * Failure: An LDAP error code.
622 *
623 * NOTES
624 * LDAP function that needs it is called.
625 */
626 ULONG CDECL ldap_start_tls_sW( WLDAP32_LDAP *ld, PULONG retval, WLDAP32_LDAPMessage **result,
627 PLDAPControlW *serverctrls, PLDAPControlW *clientctrls )
628 {
629 ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
630 #ifdef HAVE_LDAP
631 LDAPControl **serverctrlsU = NULL, **clientctrlsU = NULL;
632
633 ret = WLDAP32_LDAP_NO_MEMORY;
634
635 TRACE( "(%p, %p, %p, %p, %p)\n", ld, retval, result, serverctrls, clientctrls );
636
637 if (!ld) return ~0u;
638
639 if (serverctrls) {
640 serverctrlsU = controlarrayWtoU( serverctrls );
641 if (!serverctrlsU) goto exit;
642 }
643 if (clientctrls) {
644 clientctrlsU = controlarrayWtoU( clientctrls );
645 if (!clientctrlsU) goto exit;
646 }
647
648 ret = map_error( ldap_start_tls_s( ld, serverctrlsU, clientctrlsU ));
649
650 exit:
651 controlarrayfreeU( serverctrlsU );
652 controlarrayfreeU( clientctrlsU );
653
654 #endif
655 return ret;
656 }
657
658 /***********************************************************************
659 * ldap_startup (WLDAP32.@)
660 */
661 ULONG CDECL ldap_startup( PLDAP_VERSION_INFO version, HANDLE *instance )
662 {
663 TRACE( "(%p, %p)\n", version, instance );
664 return WLDAP32_LDAP_SUCCESS;
665 }
666
667 /***********************************************************************
668 * ldap_stop_tls_s (WLDAP32.@)
669 *
670 * Stop TLS encryption on an LDAP connection.
671 *
672 * PARAMS
673 * ld [I] Pointer to an LDAP context.
674 *
675 * RETURNS
676 * Success: TRUE
677 * Failure: FALSE
678 */
679 BOOLEAN CDECL ldap_stop_tls_s( WLDAP32_LDAP *ld )
680 {
681 TRACE( "(%p)\n", ld );
682 return TRUE; /* FIXME: find a way to stop tls on a connection */
683 }