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