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