4 * Lightweight Directory Access Protocol interface class.
6 * Portable Windows Library
8 * Copyright (c) 1993-2003 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
27 * Revision 1.15 2004/02/23 23:52:19 csoutheren
28 * Added pragmas to avoid every Windows application needing to include libs explicitly
30 * Revision 1.14 2004/02/04 09:37:00 rjongbloed
31 * Fixed memory leak and race condition, thanks Rossano Ravelli
33 * Revision 1.13 2004/01/17 17:45:29 csoutheren
34 * Changed to use PString::MakeEmpty
36 * Revision 1.12 2003/07/15 12:12:11 csoutheren
37 * Added support for multiple values in a single attribute string
38 * Thanks to Ravelli Rossano
40 * Revision 1.11 2003/07/12 00:10:40 csoutheren
41 * Fixed problem where Modify routines were calling Add, thanks to Ravelli Rossano
43 * Revision 1.10 2003/06/06 09:14:01 dsandras
45 * Test that a search result has been returned before calling ldapresult2error.
47 * Revision 1.9 2003/06/05 23:17:52 rjongbloed
48 * Changed default operation timeout to 30 seconds.
50 * Revision 1.8 2003/06/05 05:29:30 rjongbloed
51 * Fixed LDAP bind authentication methods, thanks Ravelli Rossano
53 * Revision 1.7 2003/04/17 08:34:48 robertj
54 * Changed LDAP structure output so if field is empty it leaves it out
55 * altogether rather then encoding an empty string, some servers barf.
57 * Revision 1.6 2003/04/16 08:00:19 robertj
58 * Windoes psuedo autoconf support
60 * Revision 1.5 2003/04/07 11:59:52 robertj
61 * Fixed search function returning an error if can't find anything for filter.
63 * Revision 1.4 2003/04/01 07:05:16 robertj
64 * Added ability to specify host:port in opening an LDAP server
66 * Revision 1.3 2003/03/31 03:32:53 robertj
67 * Major addition of functionality.
72 #pragma implementation "pldap.h"
77 #include <ptlib/sockets.h>
78 #include <ptclib/pldap.h>
86 #pragma comment(lib, P_LDAP_LIBRARY)
89 #pragma comment(linker, "/delayload:openldapd.dll")
91 #pragma comment(linker, "/delayload:openldap.dll")
93 #pragma comment(lib, "Delayimp.lib")
96 ///////////////////////////////////////////////////////////////////////////////
98 PLDAPSession::PLDAPSession(const PString
& baseDN
)
100 errorNumber(LDAP_SUCCESS
),
101 protocolVersion(LDAP_VERSION3
),
102 defaultBaseDN(baseDN
),
103 searchLimit(UINT_MAX
),
105 multipleValueSeparator('\n')
110 PLDAPSession::~PLDAPSession()
116 BOOL
PLDAPSession::Open(const PString
& server
, WORD port
)
120 PString host
= server
;
121 PINDEX colon
= server
.Find(':');
122 if (colon
!= P_MAX_INDEX
) {
123 host
= server
.Left(colon
);
124 port
= PIPSocket::GetPortByService(server
.Mid(colon
+1), "tcp");
127 ldapContext
= ldap_init(server
, port
);
131 SetOption(LDAP_OPT_PROTOCOL_VERSION
, protocolVersion
);
136 BOOL
PLDAPSession::Close()
141 ldap_unbind(ldapContext
);
147 BOOL
PLDAPSession::SetOption(int optcode
, int value
)
152 return ldap_set_option(ldapContext
, optcode
, &value
);
156 BOOL
PLDAPSession::SetOption(int optcode
, void * value
)
161 return ldap_set_option(ldapContext
, optcode
, value
);
165 BOOL
PLDAPSession::Bind(const PString
& who
,
166 const PString
& passwd
,
167 AuthenticationMethod authMethod
)
178 static const int AuthMethodCode
[NumAuthenticationMethod
] = {
179 LDAP_AUTH_SIMPLE
, LDAP_AUTH_SASL
, LDAP_AUTH_KRBV4
181 errorNumber
= ldap_bind_s(ldapContext
, whoPtr
, passwd
, AuthMethodCode
[authMethod
]);
182 return errorNumber
== LDAP_SUCCESS
;
186 PLDAPSession::ModAttrib::ModAttrib(const PString
& n
, Operation o
)
193 void PLDAPSession::ModAttrib::SetLDAPMod(struct ldapmod
& mod
, Operation defaultOp
)
195 mod
.mod_type
= (char *)(const char *)name
;
197 Operation realOp
= op
== NumOperations
? defaultOp
: op
;
198 static const int OpCode
[NumOperations
] = {
199 LDAP_MOD_ADD
, LDAP_MOD_REPLACE
, LDAP_MOD_DELETE
201 mod
.mod_op
= OpCode
[realOp
];
204 mod
.mod_op
|= LDAP_MOD_BVALUES
;
210 PLDAPSession::StringModAttrib::StringModAttrib(const PString
& name
,
212 : ModAttrib(name
, op
)
217 PLDAPSession::StringModAttrib::StringModAttrib(const PString
& name
,
218 const PString
& value
,
220 : ModAttrib(name
, op
)
226 PLDAPSession::StringModAttrib::StringModAttrib(const PString
& name
,
227 const PStringList
& vals
,
229 : ModAttrib(name
, op
),
235 void PLDAPSession::StringModAttrib::SetValue(const PString
& value
)
238 values
.AppendString(value
);
242 void PLDAPSession::StringModAttrib::AddValue(const PString
& value
)
244 values
.AppendString(value
);
248 BOOL
PLDAPSession::StringModAttrib::IsBinary() const
254 void PLDAPSession::StringModAttrib::SetLDAPModVars(struct ldapmod
& mod
)
256 pointers
.SetSize(values
.GetSize()+1);
258 for (i
= 0; i
< values
.GetSize(); i
++)
259 pointers
[i
] = values
[i
].GetPointer();
261 mod
.mod_values
= pointers
.GetPointer();
265 PLDAPSession::BinaryModAttrib::BinaryModAttrib(const PString
& name
,
267 : ModAttrib(name
, op
)
272 PLDAPSession::BinaryModAttrib::BinaryModAttrib(const PString
& name
,
273 const PBYTEArray
& value
,
275 : ModAttrib(name
, op
)
281 PLDAPSession::BinaryModAttrib::BinaryModAttrib(const PString
& name
,
282 const PList
<PBYTEArray
> & vals
,
284 : ModAttrib(name
, op
),
290 void PLDAPSession::BinaryModAttrib::SetValue(const PBYTEArray
& value
)
293 values
.Append(new PBYTEArray(value
));
297 void PLDAPSession::BinaryModAttrib::AddValue(const PBYTEArray
& value
)
299 values
.Append(new PBYTEArray(value
));
303 BOOL
PLDAPSession::BinaryModAttrib::IsBinary() const
309 void PLDAPSession::BinaryModAttrib::SetLDAPModVars(struct ldapmod
& mod
)
311 pointers
.SetSize(values
.GetSize()+1);
312 bervals
.SetSize(values
.GetSize()*sizeof(berval
));
313 berval
* ber
= (berval
*)bervals
.GetPointer();
315 for (i
= 0; i
< values
.GetSize(); i
++) {
316 ber
[i
].bv_val
= (char *)values
[i
].GetPointer();
317 ber
[i
].bv_len
= values
[i
].GetSize();
318 pointers
[i
] = &ber
[i
];
321 mod
.mod_bvalues
= pointers
.GetPointer();
325 static LDAPMod
** CreateLDAPModArray(const PList
<PLDAPSession::ModAttrib
> & attributes
,
326 PLDAPSession::ModAttrib::Operation defaultOp
,
327 PBYTEArray
& storage
)
329 PINDEX count
= attributes
.GetSize();
330 storage
.SetSize(count
*sizeof(LDAPMod
) + (count
+1)*sizeof(LDAPMod
*));
332 LDAPMod
** attrs
= (LDAPMod
**)storage
.GetPointer();
333 LDAPMod
* attr
= (LDAPMod
* )&attrs
[count
+1];
334 for (PINDEX i
= 0; i
< count
; i
++) {
336 attributes
[i
].SetLDAPMod(attr
[i
], defaultOp
);
343 static PList
<PLDAPSession::ModAttrib
> AttribsFromDict(const PStringToString
& attributes
)
345 PList
<PLDAPSession::ModAttrib
> attrs
;
347 for (PINDEX i
= 0; i
< attributes
.GetSize(); i
++)
348 attrs
.Append(new PLDAPSession::StringModAttrib(attributes
.GetKeyAt(i
),
349 attributes
.GetDataAt(i
).Lines()));
355 static PList
<PLDAPSession::ModAttrib
> AttribsFromArray(const PStringArray
& attributes
)
357 PList
<PLDAPSession::ModAttrib
> attrs
;
359 for (PINDEX i
= 0; i
< attributes
.GetSize(); i
++) {
360 PString attr
= attributes
[i
];
361 PINDEX equal
= attr
.Find('=');
362 if (equal
!= P_MAX_INDEX
)
363 attrs
.Append(new PLDAPSession::StringModAttrib(attr
.Left(equal
),
364 attr
.Mid(equal
+1).Lines()));
371 static PList
<PLDAPSession::ModAttrib
> AttribsFromStruct(const PLDAPStructBase
& attributes
)
373 PList
<PLDAPSession::ModAttrib
> attrs
;
375 for (PINDEX i
= 0; i
< attributes
.GetNumAttributes(); i
++) {
376 PLDAPAttributeBase
& attr
= attributes
.GetAttribute(i
);
378 attrs
.Append(new PLDAPSession::BinaryModAttrib(attr
.GetName(), attr
.ToBinary()));
380 PString str
= attr
.ToString();
382 attrs
.Append(new PLDAPSession::StringModAttrib(attr
.GetName(), str
));
390 BOOL
PLDAPSession::Add(const PString
& dn
, const PList
<ModAttrib
> & attributes
)
397 errorNumber
= ldap_add_ext(ldapContext
,
399 CreateLDAPModArray(attributes
, ModAttrib::Add
, storage
),
403 if (errorNumber
!= LDAP_SUCCESS
)
406 P_timeval tval
= timeout
;
407 LDAPMessage
* result
= NULL
;
408 ldap_result(ldapContext
, msgid
, LDAP_MSG_ALL
, tval
, &result
);
410 errorNumber
= ldap_result2error(ldapContext
, result
, TRUE
);
412 return errorNumber
== LDAP_SUCCESS
;
416 BOOL
PLDAPSession::Add(const PString
& dn
, const PStringToString
& attributes
)
418 return Add(dn
, AttribsFromDict(attributes
));
422 BOOL
PLDAPSession::Add(const PString
& dn
, const PStringArray
& attributes
)
424 return Add(dn
, AttribsFromArray(attributes
));
428 BOOL
PLDAPSession::Add(const PString
& dn
, const PLDAPStructBase
& attributes
)
430 return Add(dn
, AttribsFromStruct(attributes
));
434 BOOL
PLDAPSession::Modify(const PString
& dn
, const PList
<ModAttrib
> & attributes
)
441 errorNumber
= ldap_modify_ext(ldapContext
,
443 CreateLDAPModArray(attributes
, ModAttrib::Replace
, storage
),
447 if (errorNumber
!= LDAP_SUCCESS
)
450 P_timeval tval
= timeout
;
451 LDAPMessage
* result
= NULL
;
452 ldap_result(ldapContext
, msgid
, LDAP_MSG_ALL
, tval
, &result
);
454 errorNumber
= ldap_result2error(ldapContext
, result
, TRUE
);
456 return errorNumber
== LDAP_SUCCESS
;
460 BOOL
PLDAPSession::Modify(const PString
& dn
, const PStringToString
& attributes
)
462 return Modify(dn
, AttribsFromDict(attributes
));
466 BOOL
PLDAPSession::Modify(const PString
& dn
, const PStringArray
& attributes
)
468 return Modify(dn
, AttribsFromArray(attributes
));
472 BOOL
PLDAPSession::Modify(const PString
& dn
, const PLDAPStructBase
& attributes
)
474 return Modify(dn
, AttribsFromStruct(attributes
));
478 BOOL
PLDAPSession::Delete(const PString
& dn
)
484 errorNumber
= ldap_delete_ext(ldapContext
, dn
, NULL
, NULL
, &msgid
);
485 if (errorNumber
!= LDAP_SUCCESS
)
488 P_timeval tval
= timeout
;
489 LDAPMessage
* result
= NULL
;
490 ldap_result(ldapContext
, msgid
, LDAP_MSG_ALL
, tval
, &result
);
492 errorNumber
= ldap_result2error(ldapContext
, result
, TRUE
);
494 return errorNumber
== LDAP_SUCCESS
;
498 PLDAPSession::SearchContext::SearchContext()
507 PLDAPSession::SearchContext::~SearchContext()
510 ldap_msgfree(message
);
512 if (result
!= NULL
&& result
!= message
)
513 ldap_msgfree(result
);
517 BOOL
PLDAPSession::Search(SearchContext
& context
,
518 const PString
& filter
,
519 const PStringArray
& attributes
,
520 const PString
& baseDN
,
527 char ** attribs
= attributes
.ToCharArray(&storage
);
529 PString base
= baseDN
;
531 base
= defaultBaseDN
;
533 static const int ScopeCode
[NumSearchScope
] = {
534 LDAP_SCOPE_BASE
, LDAP_SCOPE_ONELEVEL
, LDAP_SCOPE_SUBTREE
537 P_timeval tval
= timeout
;
539 errorNumber
= ldap_search_ext(ldapContext
,
551 if (errorNumber
!= LDAP_SUCCESS
)
554 if (ldap_result(ldapContext
, context
.msgid
, LDAP_MSG_ONE
, tval
, &context
.result
) > 0)
555 return GetNextSearchResult(context
);
558 errorNumber
= ldap_result2error(ldapContext
, context
.result
, TRUE
);
559 if (errorNumber
== 0)
560 errorNumber
= LDAP_OTHER
;
565 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
, PStringToString
& data
)
572 if (context
.result
== NULL
|| context
.message
== NULL
|| context
.completed
)
575 // Extract the resulting data
577 data
.SetAt("dn", GetSearchResultDN(context
));
579 BerElement
* ber
= NULL
;
580 char * attrib
= ldap_first_attribute(ldapContext
, context
.message
, &ber
);
581 while (attrib
!= NULL
) {
583 struct berval
** bvals
= ldap_get_values_len(ldapContext
, context
.message
, attrib
);
585 PString value
= data(attrib
);
587 for (PINDEX i
= 0; bvals
[i
] != NULL
; i
++ ) {
589 value
+= multipleValueSeparator
;
590 value
+= PString(bvals
[i
]->bv_val
, bvals
[i
]->bv_len
);
594 data
.SetAt(attrib
, value
);
597 ldap_memfree(attrib
);
598 attrib
= ldap_next_attribute(ldapContext
, context
.message
, ber
);
608 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
,
609 const PString
& attribute
,
617 if (context
.result
== NULL
|| context
.message
== NULL
|| context
.completed
)
620 if (attribute
== "dn") {
621 data
= GetSearchResultDN(context
);
625 char ** values
= ldap_get_values(ldapContext
, context
.message
, attribute
);
629 PINDEX count
= ldap_count_values(values
);
630 for (PINDEX i
= 0; i
< count
; i
++) {
632 data
+= multipleValueSeparator
;
636 ldap_value_free(values
);
641 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
,
642 const PString
& attribute
,
650 if (context
.result
== NULL
|| context
.message
== NULL
|| context
.completed
)
653 if (attribute
== "dn") {
655 data
[0] = GetSearchResultDN(context
);
659 char ** values
= ldap_get_values(ldapContext
, context
.message
, attribute
);
663 PINDEX count
= ldap_count_values(values
);
665 for (PINDEX i
= 0; i
< count
; i
++)
668 ldap_value_free(values
);
673 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
,
674 const PString
& attribute
,
675 PArray
<PBYTEArray
> & data
)
682 if (attribute
== "dn") {
683 char * dn
= ldap_get_dn(ldapContext
, context
.message
);
684 data
.Append(new PBYTEArray((const BYTE
*)dn
, ::strlen(dn
)));
689 struct berval
** values
= ldap_get_values_len(ldapContext
, context
.message
, attribute
);
693 PINDEX count
= ldap_count_values_len(values
);
695 for (PINDEX i
= 0; i
< count
; i
++)
696 data
[i
] = PBYTEArray((const BYTE
*)values
[i
]->bv_val
, values
[i
]->bv_len
);
698 ldap_value_free_len(values
);
703 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
,
704 PLDAPStructBase
& data
)
709 BOOL atLeastOne
= FALSE
;
711 for (PINDEX i
= 0; i
< data
.GetNumAttributes(); i
++) {
712 PLDAPAttributeBase
& attr
= data
.GetAttribute(i
);
713 if (attr
.IsBinary()) {
714 PArray
<PBYTEArray
> bin
;
715 if (GetSearchResult(context
, attr
.GetName(), bin
)) {
716 attr
.FromBinary(bin
);
722 if (GetSearchResult(context
, attr
.GetName(), str
)) {
723 attr
.FromString(str
);
733 PString
PLDAPSession::GetSearchResultDN(SearchContext
& context
)
737 if (context
.message
!= NULL
) {
738 char * dn
= ldap_get_dn(ldapContext
, context
.message
);
749 BOOL
PLDAPSession::GetNextSearchResult(SearchContext
& context
)
754 if (context
.result
== NULL
|| context
.completed
)
757 P_timeval tval
= timeout
;
759 if (context
.message
== NULL
)
760 context
.message
= ldap_first_message(ldapContext
, context
.result
);
762 context
.message
= ldap_next_message(ldapContext
, context
.message
);
764 if (context
.message
!= NULL
) {
765 switch (ldap_msgtype(context
.message
)) {
766 case LDAP_RES_SEARCH_ENTRY
:
767 context
.found
= TRUE
;
768 errorNumber
= LDAP_SUCCESS
;
771 case LDAP_RES_SEARCH_RESULT
:
772 errorNumber
= ldap_result2error(ldapContext
, context
.message
, FALSE
);
773 if (errorNumber
== 0 && !context
.found
)
774 errorNumber
= LDAP_NO_RESULTS_RETURNED
;
775 context
.completed
= TRUE
;
778 // Ignore other result message types for now ...
781 ldap_msgfree(context
.result
);
782 } while (ldap_result(ldapContext
, context
.msgid
, LDAP_MSG_ONE
, tval
, &context
.result
) > 0);
785 errorNumber
= ldap_result2error(ldapContext
, context
.result
, FALSE
);
786 if (errorNumber
== 0)
787 errorNumber
= LDAP_OTHER
;
792 PList
<PStringToString
> PLDAPSession::Search(const PString
& filter
,
793 const PStringArray
& attributes
,
794 const PString
& base
,
797 PList
<PStringToString
> data
;
799 SearchContext context
;
800 if (!Search(context
, filter
, attributes
, base
, scope
))
804 PStringToString
* entry
= new PStringToString
;
805 if (GetSearchResult(context
, *entry
))
811 } while (GetNextSearchResult(context
));
817 PString
PLDAPSession::GetErrorText() const
819 return ldap_err2string(errorNumber
);
823 ///////////////////////////////////////////////////////////////////////////////
825 PLDAPAttributeBase::PLDAPAttributeBase(const char * n
, void * ptr
, PINDEX sz
)
830 PLDAPStructBase::GetInitialiser().AddAttribute(this);
834 PString
PLDAPAttributeBase::ToString() const
836 PStringStream stream
;
842 void PLDAPAttributeBase::FromString(const PString
& str
)
844 PStringStream
stream(str
);
849 PBYTEArray
PLDAPAttributeBase::ToBinary() const
851 return PBYTEArray((const BYTE
*)pointer
, size
, FALSE
);
855 void PLDAPAttributeBase::FromBinary(const PArray
<PBYTEArray
> & data
)
857 if (data
.GetSize() > 0 && data
[0].GetSize() == size
)
858 memcpy(pointer
, data
[0], size
);
862 ///////////////////////////////////////////////////////////////////////////////
864 PMutex
PLDAPStructBase::initialiserMutex
;
865 PLDAPStructBase
* PLDAPStructBase::initialiserInstance
;
867 PLDAPStructBase::PLDAPStructBase()
869 attributes
.DisallowDeleteObjects();
871 initialiserMutex
.Wait();
872 initialiserStack
= initialiserInstance
;
873 initialiserInstance
= this;
877 PLDAPStructBase
& PLDAPStructBase::operator=(const PLDAPStructBase
& other
)
879 for (PINDEX i
= 0; i
< attributes
.GetSize(); i
++)
880 attributes
.GetDataAt(i
).Copy(other
.attributes
.GetDataAt(i
));
886 PLDAPStructBase
& PLDAPStructBase::operator=(const PStringArray
& array
)
888 for (PINDEX i
= 0; i
< array
.GetSize(); i
++) {
889 PString str
= array
[i
];
890 PINDEX equal
= str
.Find('=');
891 if (equal
!= P_MAX_INDEX
) {
892 PLDAPAttributeBase
* attr
= GetAttribute(str
.Left(equal
));
894 attr
->FromString(str
.Mid(equal
+1));
901 PLDAPStructBase
& PLDAPStructBase::operator=(const PStringToString
& dict
)
903 for (PINDEX i
= 0; i
< dict
.GetSize(); i
++) {
904 PLDAPAttributeBase
* attr
= GetAttribute(dict
.GetKeyAt(i
));
906 attr
->FromString(dict
.GetDataAt(i
));
912 void PLDAPStructBase::PrintOn(ostream
& strm
) const
914 strm
<< attributes
<< '\n';
918 void PLDAPStructBase::AddAttribute(PLDAPAttributeBase
* attr
)
920 attributes
.SetAt(attr
->GetName(), attr
);
924 void PLDAPStructBase::EndConstructor()
926 initialiserInstance
= initialiserStack
;
927 initialiserMutex
.Signal();
934 // End of file ////////////////////////////////////////////////////////////////