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.17 2004/05/24 12:02:49 csoutheren
28 * Add function to permit setting a limit on the number of results returned
29 * from an LDAP query. Change the default number of results to unlimited,
30 * rather than MAX_INT which apparently is clamped to some arbitrary low value.
31 * Thanks to Damien Sandras
33 * Revision 1.16 2004/04/09 06:52:17 rjongbloed
34 * Removed #pargma linker command for /delayload of DLL as documentations sais that
37 * Revision 1.15 2004/02/23 23:52:19 csoutheren
38 * Added pragmas to avoid every Windows application needing to include libs explicitly
40 * Revision 1.14 2004/02/04 09:37:00 rjongbloed
41 * Fixed memory leak and race condition, thanks Rossano Ravelli
43 * Revision 1.13 2004/01/17 17:45:29 csoutheren
44 * Changed to use PString::MakeEmpty
46 * Revision 1.12 2003/07/15 12:12:11 csoutheren
47 * Added support for multiple values in a single attribute string
48 * Thanks to Ravelli Rossano
50 * Revision 1.11 2003/07/12 00:10:40 csoutheren
51 * Fixed problem where Modify routines were calling Add, thanks to Ravelli Rossano
53 * Revision 1.10 2003/06/06 09:14:01 dsandras
55 * Test that a search result has been returned before calling ldapresult2error.
57 * Revision 1.9 2003/06/05 23:17:52 rjongbloed
58 * Changed default operation timeout to 30 seconds.
60 * Revision 1.8 2003/06/05 05:29:30 rjongbloed
61 * Fixed LDAP bind authentication methods, thanks Ravelli Rossano
63 * Revision 1.7 2003/04/17 08:34:48 robertj
64 * Changed LDAP structure output so if field is empty it leaves it out
65 * altogether rather then encoding an empty string, some servers barf.
67 * Revision 1.6 2003/04/16 08:00:19 robertj
68 * Windoes psuedo autoconf support
70 * Revision 1.5 2003/04/07 11:59:52 robertj
71 * Fixed search function returning an error if can't find anything for filter.
73 * Revision 1.4 2003/04/01 07:05:16 robertj
74 * Added ability to specify host:port in opening an LDAP server
76 * Revision 1.3 2003/03/31 03:32:53 robertj
77 * Major addition of functionality.
82 #pragma implementation "pldap.h"
87 #include <ptlib/sockets.h>
88 #include <ptclib/pldap.h>
96 #pragma comment(lib, P_LDAP_LIBRARY)
99 ///////////////////////////////////////////////////////////////////////////////
101 PLDAPSession::PLDAPSession(const PString
& baseDN
)
103 errorNumber(LDAP_SUCCESS
),
104 protocolVersion(LDAP_VERSION3
),
105 defaultBaseDN(baseDN
),
108 multipleValueSeparator('\n')
113 PLDAPSession::~PLDAPSession()
119 BOOL
PLDAPSession::Open(const PString
& server
, WORD port
)
123 PString host
= server
;
124 PINDEX colon
= server
.Find(':');
125 if (colon
!= P_MAX_INDEX
) {
126 host
= server
.Left(colon
);
127 port
= PIPSocket::GetPortByService(server
.Mid(colon
+1), "tcp");
130 ldapContext
= ldap_init(server
, port
);
134 SetOption(LDAP_OPT_PROTOCOL_VERSION
, protocolVersion
);
139 BOOL
PLDAPSession::Close()
144 ldap_unbind(ldapContext
);
150 BOOL
PLDAPSession::SetOption(int optcode
, int value
)
155 return ldap_set_option(ldapContext
, optcode
, &value
);
159 BOOL
PLDAPSession::SetOption(int optcode
, void * value
)
164 return ldap_set_option(ldapContext
, optcode
, value
);
168 BOOL
PLDAPSession::Bind(const PString
& who
,
169 const PString
& passwd
,
170 AuthenticationMethod authMethod
)
181 static const int AuthMethodCode
[NumAuthenticationMethod
] = {
182 LDAP_AUTH_SIMPLE
, LDAP_AUTH_SASL
, LDAP_AUTH_KRBV4
184 errorNumber
= ldap_bind_s(ldapContext
, whoPtr
, passwd
, AuthMethodCode
[authMethod
]);
185 return errorNumber
== LDAP_SUCCESS
;
189 PLDAPSession::ModAttrib::ModAttrib(const PString
& n
, Operation o
)
196 void PLDAPSession::ModAttrib::SetLDAPMod(struct ldapmod
& mod
, Operation defaultOp
)
198 mod
.mod_type
= (char *)(const char *)name
;
200 Operation realOp
= op
== NumOperations
? defaultOp
: op
;
201 static const int OpCode
[NumOperations
] = {
202 LDAP_MOD_ADD
, LDAP_MOD_REPLACE
, LDAP_MOD_DELETE
204 mod
.mod_op
= OpCode
[realOp
];
207 mod
.mod_op
|= LDAP_MOD_BVALUES
;
213 PLDAPSession::StringModAttrib::StringModAttrib(const PString
& name
,
215 : ModAttrib(name
, op
)
220 PLDAPSession::StringModAttrib::StringModAttrib(const PString
& name
,
221 const PString
& value
,
223 : ModAttrib(name
, op
)
229 PLDAPSession::StringModAttrib::StringModAttrib(const PString
& name
,
230 const PStringList
& vals
,
232 : ModAttrib(name
, op
),
238 void PLDAPSession::StringModAttrib::SetValue(const PString
& value
)
241 values
.AppendString(value
);
245 void PLDAPSession::StringModAttrib::AddValue(const PString
& value
)
247 values
.AppendString(value
);
251 BOOL
PLDAPSession::StringModAttrib::IsBinary() const
257 void PLDAPSession::StringModAttrib::SetLDAPModVars(struct ldapmod
& mod
)
259 pointers
.SetSize(values
.GetSize()+1);
261 for (i
= 0; i
< values
.GetSize(); i
++)
262 pointers
[i
] = values
[i
].GetPointer();
264 mod
.mod_values
= pointers
.GetPointer();
268 PLDAPSession::BinaryModAttrib::BinaryModAttrib(const PString
& name
,
270 : ModAttrib(name
, op
)
275 PLDAPSession::BinaryModAttrib::BinaryModAttrib(const PString
& name
,
276 const PBYTEArray
& value
,
278 : ModAttrib(name
, op
)
284 PLDAPSession::BinaryModAttrib::BinaryModAttrib(const PString
& name
,
285 const PList
<PBYTEArray
> & vals
,
287 : ModAttrib(name
, op
),
293 void PLDAPSession::BinaryModAttrib::SetValue(const PBYTEArray
& value
)
296 values
.Append(new PBYTEArray(value
));
300 void PLDAPSession::BinaryModAttrib::AddValue(const PBYTEArray
& value
)
302 values
.Append(new PBYTEArray(value
));
306 BOOL
PLDAPSession::BinaryModAttrib::IsBinary() const
312 void PLDAPSession::BinaryModAttrib::SetLDAPModVars(struct ldapmod
& mod
)
314 pointers
.SetSize(values
.GetSize()+1);
315 bervals
.SetSize(values
.GetSize()*sizeof(berval
));
316 berval
* ber
= (berval
*)bervals
.GetPointer();
318 for (i
= 0; i
< values
.GetSize(); i
++) {
319 ber
[i
].bv_val
= (char *)values
[i
].GetPointer();
320 ber
[i
].bv_len
= values
[i
].GetSize();
321 pointers
[i
] = &ber
[i
];
324 mod
.mod_bvalues
= pointers
.GetPointer();
328 static LDAPMod
** CreateLDAPModArray(const PList
<PLDAPSession::ModAttrib
> & attributes
,
329 PLDAPSession::ModAttrib::Operation defaultOp
,
330 PBYTEArray
& storage
)
332 PINDEX count
= attributes
.GetSize();
333 storage
.SetSize(count
*sizeof(LDAPMod
) + (count
+1)*sizeof(LDAPMod
*));
335 LDAPMod
** attrs
= (LDAPMod
**)storage
.GetPointer();
336 LDAPMod
* attr
= (LDAPMod
* )&attrs
[count
+1];
337 for (PINDEX i
= 0; i
< count
; i
++) {
339 attributes
[i
].SetLDAPMod(attr
[i
], defaultOp
);
346 static PList
<PLDAPSession::ModAttrib
> AttribsFromDict(const PStringToString
& attributes
)
348 PList
<PLDAPSession::ModAttrib
> attrs
;
350 for (PINDEX i
= 0; i
< attributes
.GetSize(); i
++)
351 attrs
.Append(new PLDAPSession::StringModAttrib(attributes
.GetKeyAt(i
),
352 attributes
.GetDataAt(i
).Lines()));
358 static PList
<PLDAPSession::ModAttrib
> AttribsFromArray(const PStringArray
& attributes
)
360 PList
<PLDAPSession::ModAttrib
> attrs
;
362 for (PINDEX i
= 0; i
< attributes
.GetSize(); i
++) {
363 PString attr
= attributes
[i
];
364 PINDEX equal
= attr
.Find('=');
365 if (equal
!= P_MAX_INDEX
)
366 attrs
.Append(new PLDAPSession::StringModAttrib(attr
.Left(equal
),
367 attr
.Mid(equal
+1).Lines()));
374 static PList
<PLDAPSession::ModAttrib
> AttribsFromStruct(const PLDAPStructBase
& attributes
)
376 PList
<PLDAPSession::ModAttrib
> attrs
;
378 for (PINDEX i
= 0; i
< attributes
.GetNumAttributes(); i
++) {
379 PLDAPAttributeBase
& attr
= attributes
.GetAttribute(i
);
381 attrs
.Append(new PLDAPSession::BinaryModAttrib(attr
.GetName(), attr
.ToBinary()));
383 PString str
= attr
.ToString();
385 attrs
.Append(new PLDAPSession::StringModAttrib(attr
.GetName(), str
));
393 BOOL
PLDAPSession::Add(const PString
& dn
, const PList
<ModAttrib
> & attributes
)
400 errorNumber
= ldap_add_ext(ldapContext
,
402 CreateLDAPModArray(attributes
, ModAttrib::Add
, storage
),
406 if (errorNumber
!= LDAP_SUCCESS
)
409 P_timeval tval
= timeout
;
410 LDAPMessage
* result
= NULL
;
411 ldap_result(ldapContext
, msgid
, LDAP_MSG_ALL
, tval
, &result
);
413 errorNumber
= ldap_result2error(ldapContext
, result
, TRUE
);
415 return errorNumber
== LDAP_SUCCESS
;
419 BOOL
PLDAPSession::Add(const PString
& dn
, const PStringToString
& attributes
)
421 return Add(dn
, AttribsFromDict(attributes
));
425 BOOL
PLDAPSession::Add(const PString
& dn
, const PStringArray
& attributes
)
427 return Add(dn
, AttribsFromArray(attributes
));
431 BOOL
PLDAPSession::Add(const PString
& dn
, const PLDAPStructBase
& attributes
)
433 return Add(dn
, AttribsFromStruct(attributes
));
437 BOOL
PLDAPSession::Modify(const PString
& dn
, const PList
<ModAttrib
> & attributes
)
444 errorNumber
= ldap_modify_ext(ldapContext
,
446 CreateLDAPModArray(attributes
, ModAttrib::Replace
, storage
),
450 if (errorNumber
!= LDAP_SUCCESS
)
453 P_timeval tval
= timeout
;
454 LDAPMessage
* result
= NULL
;
455 ldap_result(ldapContext
, msgid
, LDAP_MSG_ALL
, tval
, &result
);
457 errorNumber
= ldap_result2error(ldapContext
, result
, TRUE
);
459 return errorNumber
== LDAP_SUCCESS
;
463 BOOL
PLDAPSession::Modify(const PString
& dn
, const PStringToString
& attributes
)
465 return Modify(dn
, AttribsFromDict(attributes
));
469 BOOL
PLDAPSession::Modify(const PString
& dn
, const PStringArray
& attributes
)
471 return Modify(dn
, AttribsFromArray(attributes
));
475 BOOL
PLDAPSession::Modify(const PString
& dn
, const PLDAPStructBase
& attributes
)
477 return Modify(dn
, AttribsFromStruct(attributes
));
481 BOOL
PLDAPSession::Delete(const PString
& dn
)
487 errorNumber
= ldap_delete_ext(ldapContext
, dn
, NULL
, NULL
, &msgid
);
488 if (errorNumber
!= LDAP_SUCCESS
)
491 P_timeval tval
= timeout
;
492 LDAPMessage
* result
= NULL
;
493 ldap_result(ldapContext
, msgid
, LDAP_MSG_ALL
, tval
, &result
);
495 errorNumber
= ldap_result2error(ldapContext
, result
, TRUE
);
497 return errorNumber
== LDAP_SUCCESS
;
501 PLDAPSession::SearchContext::SearchContext()
510 PLDAPSession::SearchContext::~SearchContext()
513 ldap_msgfree(message
);
515 if (result
!= NULL
&& result
!= message
)
516 ldap_msgfree(result
);
520 BOOL
PLDAPSession::Search(SearchContext
& context
,
521 const PString
& filter
,
522 const PStringArray
& attributes
,
523 const PString
& baseDN
,
530 char ** attribs
= attributes
.ToCharArray(&storage
);
532 PString base
= baseDN
;
534 base
= defaultBaseDN
;
536 static const int ScopeCode
[NumSearchScope
] = {
537 LDAP_SCOPE_BASE
, LDAP_SCOPE_ONELEVEL
, LDAP_SCOPE_SUBTREE
540 P_timeval tval
= timeout
;
542 errorNumber
= ldap_search_ext(ldapContext
,
554 if (errorNumber
!= LDAP_SUCCESS
)
557 if (ldap_result(ldapContext
, context
.msgid
, LDAP_MSG_ONE
, tval
, &context
.result
) > 0)
558 return GetNextSearchResult(context
);
561 errorNumber
= ldap_result2error(ldapContext
, context
.result
, TRUE
);
562 if (errorNumber
== 0)
563 errorNumber
= LDAP_OTHER
;
568 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
, PStringToString
& data
)
575 if (context
.result
== NULL
|| context
.message
== NULL
|| context
.completed
)
578 // Extract the resulting data
580 data
.SetAt("dn", GetSearchResultDN(context
));
582 BerElement
* ber
= NULL
;
583 char * attrib
= ldap_first_attribute(ldapContext
, context
.message
, &ber
);
584 while (attrib
!= NULL
) {
586 struct berval
** bvals
= ldap_get_values_len(ldapContext
, context
.message
, attrib
);
588 PString value
= data(attrib
);
590 for (PINDEX i
= 0; bvals
[i
] != NULL
; i
++ ) {
592 value
+= multipleValueSeparator
;
593 value
+= PString(bvals
[i
]->bv_val
, bvals
[i
]->bv_len
);
597 data
.SetAt(attrib
, value
);
600 ldap_memfree(attrib
);
601 attrib
= ldap_next_attribute(ldapContext
, context
.message
, ber
);
611 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
,
612 const PString
& attribute
,
620 if (context
.result
== NULL
|| context
.message
== NULL
|| context
.completed
)
623 if (attribute
== "dn") {
624 data
= GetSearchResultDN(context
);
628 char ** values
= ldap_get_values(ldapContext
, context
.message
, attribute
);
632 PINDEX count
= ldap_count_values(values
);
633 for (PINDEX i
= 0; i
< count
; i
++) {
635 data
+= multipleValueSeparator
;
639 ldap_value_free(values
);
644 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
,
645 const PString
& attribute
,
653 if (context
.result
== NULL
|| context
.message
== NULL
|| context
.completed
)
656 if (attribute
== "dn") {
658 data
[0] = GetSearchResultDN(context
);
662 char ** values
= ldap_get_values(ldapContext
, context
.message
, attribute
);
666 PINDEX count
= ldap_count_values(values
);
668 for (PINDEX i
= 0; i
< count
; i
++)
671 ldap_value_free(values
);
676 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
,
677 const PString
& attribute
,
678 PArray
<PBYTEArray
> & data
)
685 if (attribute
== "dn") {
686 char * dn
= ldap_get_dn(ldapContext
, context
.message
);
687 data
.Append(new PBYTEArray((const BYTE
*)dn
, ::strlen(dn
)));
692 struct berval
** values
= ldap_get_values_len(ldapContext
, context
.message
, attribute
);
696 PINDEX count
= ldap_count_values_len(values
);
698 for (PINDEX i
= 0; i
< count
; i
++)
699 data
[i
] = PBYTEArray((const BYTE
*)values
[i
]->bv_val
, values
[i
]->bv_len
);
701 ldap_value_free_len(values
);
706 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
,
707 PLDAPStructBase
& data
)
712 BOOL atLeastOne
= FALSE
;
714 for (PINDEX i
= 0; i
< data
.GetNumAttributes(); i
++) {
715 PLDAPAttributeBase
& attr
= data
.GetAttribute(i
);
716 if (attr
.IsBinary()) {
717 PArray
<PBYTEArray
> bin
;
718 if (GetSearchResult(context
, attr
.GetName(), bin
)) {
719 attr
.FromBinary(bin
);
725 if (GetSearchResult(context
, attr
.GetName(), str
)) {
726 attr
.FromString(str
);
736 PString
PLDAPSession::GetSearchResultDN(SearchContext
& context
)
740 if (context
.message
!= NULL
) {
741 char * dn
= ldap_get_dn(ldapContext
, context
.message
);
752 BOOL
PLDAPSession::GetNextSearchResult(SearchContext
& context
)
757 if (context
.result
== NULL
|| context
.completed
)
760 P_timeval tval
= timeout
;
762 if (context
.message
== NULL
)
763 context
.message
= ldap_first_message(ldapContext
, context
.result
);
765 context
.message
= ldap_next_message(ldapContext
, context
.message
);
767 if (context
.message
!= NULL
) {
768 switch (ldap_msgtype(context
.message
)) {
769 case LDAP_RES_SEARCH_ENTRY
:
770 context
.found
= TRUE
;
771 errorNumber
= LDAP_SUCCESS
;
774 case LDAP_RES_SEARCH_RESULT
:
775 errorNumber
= ldap_result2error(ldapContext
, context
.message
, FALSE
);
776 if (errorNumber
== 0 && !context
.found
)
777 errorNumber
= LDAP_NO_RESULTS_RETURNED
;
778 context
.completed
= TRUE
;
781 // Ignore other result message types for now ...
784 ldap_msgfree(context
.result
);
785 } while (ldap_result(ldapContext
, context
.msgid
, LDAP_MSG_ONE
, tval
, &context
.result
) > 0);
788 errorNumber
= ldap_result2error(ldapContext
, context
.result
, FALSE
);
789 if (errorNumber
== 0)
790 errorNumber
= LDAP_OTHER
;
795 PList
<PStringToString
> PLDAPSession::Search(const PString
& filter
,
796 const PStringArray
& attributes
,
797 const PString
& base
,
800 PList
<PStringToString
> data
;
802 SearchContext context
;
803 if (!Search(context
, filter
, attributes
, base
, scope
))
807 PStringToString
* entry
= new PStringToString
;
808 if (GetSearchResult(context
, *entry
))
814 } while (GetNextSearchResult(context
));
820 PString
PLDAPSession::GetErrorText() const
822 return ldap_err2string(errorNumber
);
826 ///////////////////////////////////////////////////////////////////////////////
828 PLDAPAttributeBase::PLDAPAttributeBase(const char * n
, void * ptr
, PINDEX sz
)
833 PLDAPStructBase::GetInitialiser().AddAttribute(this);
837 PString
PLDAPAttributeBase::ToString() const
839 PStringStream stream
;
845 void PLDAPAttributeBase::FromString(const PString
& str
)
847 PStringStream
stream(str
);
852 PBYTEArray
PLDAPAttributeBase::ToBinary() const
854 return PBYTEArray((const BYTE
*)pointer
, size
, FALSE
);
858 void PLDAPAttributeBase::FromBinary(const PArray
<PBYTEArray
> & data
)
860 if (data
.GetSize() > 0 && data
[0].GetSize() == size
)
861 memcpy(pointer
, data
[0], size
);
865 ///////////////////////////////////////////////////////////////////////////////
867 PMutex
PLDAPStructBase::initialiserMutex
;
868 PLDAPStructBase
* PLDAPStructBase::initialiserInstance
;
870 PLDAPStructBase::PLDAPStructBase()
872 attributes
.DisallowDeleteObjects();
874 initialiserMutex
.Wait();
875 initialiserStack
= initialiserInstance
;
876 initialiserInstance
= this;
880 PLDAPStructBase
& PLDAPStructBase::operator=(const PLDAPStructBase
& other
)
882 for (PINDEX i
= 0; i
< attributes
.GetSize(); i
++)
883 attributes
.GetDataAt(i
).Copy(other
.attributes
.GetDataAt(i
));
889 PLDAPStructBase
& PLDAPStructBase::operator=(const PStringArray
& array
)
891 for (PINDEX i
= 0; i
< array
.GetSize(); i
++) {
892 PString str
= array
[i
];
893 PINDEX equal
= str
.Find('=');
894 if (equal
!= P_MAX_INDEX
) {
895 PLDAPAttributeBase
* attr
= GetAttribute(str
.Left(equal
));
897 attr
->FromString(str
.Mid(equal
+1));
904 PLDAPStructBase
& PLDAPStructBase::operator=(const PStringToString
& dict
)
906 for (PINDEX i
= 0; i
< dict
.GetSize(); i
++) {
907 PLDAPAttributeBase
* attr
= GetAttribute(dict
.GetKeyAt(i
));
909 attr
->FromString(dict
.GetDataAt(i
));
915 void PLDAPStructBase::PrintOn(ostream
& strm
) const
917 strm
<< attributes
<< '\n';
921 void PLDAPStructBase::AddAttribute(PLDAPAttributeBase
* attr
)
923 attributes
.SetAt(attr
->GetName(), attr
);
927 void PLDAPStructBase::EndConstructor()
929 initialiserInstance
= initialiserStack
;
930 initialiserMutex
.Signal();
937 // End of file ////////////////////////////////////////////////////////////////