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.14 2004/02/04 09:37:00 rjongbloed
28 * Fixed memory leak and race condition, thanks Rossano Ravelli
30 * Revision 1.13 2004/01/17 17:45:29 csoutheren
31 * Changed to use PString::MakeEmpty
33 * Revision 1.12 2003/07/15 12:12:11 csoutheren
34 * Added support for multiple values in a single attribute string
35 * Thanks to Ravelli Rossano
37 * Revision 1.11 2003/07/12 00:10:40 csoutheren
38 * Fixed problem where Modify routines were calling Add, thanks to Ravelli Rossano
40 * Revision 1.10 2003/06/06 09:14:01 dsandras
42 * Test that a search result has been returned before calling ldapresult2error.
44 * Revision 1.9 2003/06/05 23:17:52 rjongbloed
45 * Changed default operation timeout to 30 seconds.
47 * Revision 1.8 2003/06/05 05:29:30 rjongbloed
48 * Fixed LDAP bind authentication methods, thanks Ravelli Rossano
50 * Revision 1.7 2003/04/17 08:34:48 robertj
51 * Changed LDAP structure output so if field is empty it leaves it out
52 * altogether rather then encoding an empty string, some servers barf.
54 * Revision 1.6 2003/04/16 08:00:19 robertj
55 * Windoes psuedo autoconf support
57 * Revision 1.5 2003/04/07 11:59:52 robertj
58 * Fixed search function returning an error if can't find anything for filter.
60 * Revision 1.4 2003/04/01 07:05:16 robertj
61 * Added ability to specify host:port in opening an LDAP server
63 * Revision 1.3 2003/03/31 03:32:53 robertj
64 * Major addition of functionality.
69 #pragma implementation "pldap.h"
74 #include <ptlib/sockets.h>
75 #include <ptclib/pldap.h>
83 #pragma comment(lib, P_LDAP_LIBRARY)
87 ///////////////////////////////////////////////////////////////////////////////
89 PLDAPSession::PLDAPSession(const PString
& baseDN
)
91 errorNumber(LDAP_SUCCESS
),
92 protocolVersion(LDAP_VERSION3
),
93 defaultBaseDN(baseDN
),
94 searchLimit(UINT_MAX
),
96 multipleValueSeparator('\n')
101 PLDAPSession::~PLDAPSession()
107 BOOL
PLDAPSession::Open(const PString
& server
, WORD port
)
111 PString host
= server
;
112 PINDEX colon
= server
.Find(':');
113 if (colon
!= P_MAX_INDEX
) {
114 host
= server
.Left(colon
);
115 port
= PIPSocket::GetPortByService(server
.Mid(colon
+1), "tcp");
118 ldapContext
= ldap_init(server
, port
);
122 SetOption(LDAP_OPT_PROTOCOL_VERSION
, protocolVersion
);
127 BOOL
PLDAPSession::Close()
132 ldap_unbind(ldapContext
);
138 BOOL
PLDAPSession::SetOption(int optcode
, int value
)
143 return ldap_set_option(ldapContext
, optcode
, &value
);
147 BOOL
PLDAPSession::SetOption(int optcode
, void * value
)
152 return ldap_set_option(ldapContext
, optcode
, value
);
156 BOOL
PLDAPSession::Bind(const PString
& who
,
157 const PString
& passwd
,
158 AuthenticationMethod authMethod
)
169 static const int AuthMethodCode
[NumAuthenticationMethod
] = {
170 LDAP_AUTH_SIMPLE
, LDAP_AUTH_SASL
, LDAP_AUTH_KRBV4
172 errorNumber
= ldap_bind_s(ldapContext
, whoPtr
, passwd
, AuthMethodCode
[authMethod
]);
173 return errorNumber
== LDAP_SUCCESS
;
177 PLDAPSession::ModAttrib::ModAttrib(const PString
& n
, Operation o
)
184 void PLDAPSession::ModAttrib::SetLDAPMod(struct ldapmod
& mod
, Operation defaultOp
)
186 mod
.mod_type
= (char *)(const char *)name
;
188 Operation realOp
= op
== NumOperations
? defaultOp
: op
;
189 static const int OpCode
[NumOperations
] = {
190 LDAP_MOD_ADD
, LDAP_MOD_REPLACE
, LDAP_MOD_DELETE
192 mod
.mod_op
= OpCode
[realOp
];
195 mod
.mod_op
|= LDAP_MOD_BVALUES
;
201 PLDAPSession::StringModAttrib::StringModAttrib(const PString
& name
,
203 : ModAttrib(name
, op
)
208 PLDAPSession::StringModAttrib::StringModAttrib(const PString
& name
,
209 const PString
& value
,
211 : ModAttrib(name
, op
)
217 PLDAPSession::StringModAttrib::StringModAttrib(const PString
& name
,
218 const PStringList
& vals
,
220 : ModAttrib(name
, op
),
226 void PLDAPSession::StringModAttrib::SetValue(const PString
& value
)
229 values
.AppendString(value
);
233 void PLDAPSession::StringModAttrib::AddValue(const PString
& value
)
235 values
.AppendString(value
);
239 BOOL
PLDAPSession::StringModAttrib::IsBinary() const
245 void PLDAPSession::StringModAttrib::SetLDAPModVars(struct ldapmod
& mod
)
247 pointers
.SetSize(values
.GetSize()+1);
249 for (i
= 0; i
< values
.GetSize(); i
++)
250 pointers
[i
] = values
[i
].GetPointer();
252 mod
.mod_values
= pointers
.GetPointer();
256 PLDAPSession::BinaryModAttrib::BinaryModAttrib(const PString
& name
,
258 : ModAttrib(name
, op
)
263 PLDAPSession::BinaryModAttrib::BinaryModAttrib(const PString
& name
,
264 const PBYTEArray
& value
,
266 : ModAttrib(name
, op
)
272 PLDAPSession::BinaryModAttrib::BinaryModAttrib(const PString
& name
,
273 const PList
<PBYTEArray
> & vals
,
275 : ModAttrib(name
, op
),
281 void PLDAPSession::BinaryModAttrib::SetValue(const PBYTEArray
& value
)
284 values
.Append(new PBYTEArray(value
));
288 void PLDAPSession::BinaryModAttrib::AddValue(const PBYTEArray
& value
)
290 values
.Append(new PBYTEArray(value
));
294 BOOL
PLDAPSession::BinaryModAttrib::IsBinary() const
300 void PLDAPSession::BinaryModAttrib::SetLDAPModVars(struct ldapmod
& mod
)
302 pointers
.SetSize(values
.GetSize()+1);
303 bervals
.SetSize(values
.GetSize()*sizeof(berval
));
304 berval
* ber
= (berval
*)bervals
.GetPointer();
306 for (i
= 0; i
< values
.GetSize(); i
++) {
307 ber
[i
].bv_val
= (char *)values
[i
].GetPointer();
308 ber
[i
].bv_len
= values
[i
].GetSize();
309 pointers
[i
] = &ber
[i
];
312 mod
.mod_bvalues
= pointers
.GetPointer();
316 static LDAPMod
** CreateLDAPModArray(const PList
<PLDAPSession::ModAttrib
> & attributes
,
317 PLDAPSession::ModAttrib::Operation defaultOp
,
318 PBYTEArray
& storage
)
320 PINDEX count
= attributes
.GetSize();
321 storage
.SetSize(count
*sizeof(LDAPMod
) + (count
+1)*sizeof(LDAPMod
*));
323 LDAPMod
** attrs
= (LDAPMod
**)storage
.GetPointer();
324 LDAPMod
* attr
= (LDAPMod
* )&attrs
[count
+1];
325 for (PINDEX i
= 0; i
< count
; i
++) {
327 attributes
[i
].SetLDAPMod(attr
[i
], defaultOp
);
334 static PList
<PLDAPSession::ModAttrib
> AttribsFromDict(const PStringToString
& attributes
)
336 PList
<PLDAPSession::ModAttrib
> attrs
;
338 for (PINDEX i
= 0; i
< attributes
.GetSize(); i
++)
339 attrs
.Append(new PLDAPSession::StringModAttrib(attributes
.GetKeyAt(i
),
340 attributes
.GetDataAt(i
).Lines()));
346 static PList
<PLDAPSession::ModAttrib
> AttribsFromArray(const PStringArray
& attributes
)
348 PList
<PLDAPSession::ModAttrib
> attrs
;
350 for (PINDEX i
= 0; i
< attributes
.GetSize(); i
++) {
351 PString attr
= attributes
[i
];
352 PINDEX equal
= attr
.Find('=');
353 if (equal
!= P_MAX_INDEX
)
354 attrs
.Append(new PLDAPSession::StringModAttrib(attr
.Left(equal
),
355 attr
.Mid(equal
+1).Lines()));
362 static PList
<PLDAPSession::ModAttrib
> AttribsFromStruct(const PLDAPStructBase
& attributes
)
364 PList
<PLDAPSession::ModAttrib
> attrs
;
366 for (PINDEX i
= 0; i
< attributes
.GetNumAttributes(); i
++) {
367 PLDAPAttributeBase
& attr
= attributes
.GetAttribute(i
);
369 attrs
.Append(new PLDAPSession::BinaryModAttrib(attr
.GetName(), attr
.ToBinary()));
371 PString str
= attr
.ToString();
373 attrs
.Append(new PLDAPSession::StringModAttrib(attr
.GetName(), str
));
381 BOOL
PLDAPSession::Add(const PString
& dn
, const PList
<ModAttrib
> & attributes
)
388 errorNumber
= ldap_add_ext(ldapContext
,
390 CreateLDAPModArray(attributes
, ModAttrib::Add
, storage
),
394 if (errorNumber
!= LDAP_SUCCESS
)
397 P_timeval tval
= timeout
;
398 LDAPMessage
* result
= NULL
;
399 ldap_result(ldapContext
, msgid
, LDAP_MSG_ALL
, tval
, &result
);
401 errorNumber
= ldap_result2error(ldapContext
, result
, TRUE
);
403 return errorNumber
== LDAP_SUCCESS
;
407 BOOL
PLDAPSession::Add(const PString
& dn
, const PStringToString
& attributes
)
409 return Add(dn
, AttribsFromDict(attributes
));
413 BOOL
PLDAPSession::Add(const PString
& dn
, const PStringArray
& attributes
)
415 return Add(dn
, AttribsFromArray(attributes
));
419 BOOL
PLDAPSession::Add(const PString
& dn
, const PLDAPStructBase
& attributes
)
421 return Add(dn
, AttribsFromStruct(attributes
));
425 BOOL
PLDAPSession::Modify(const PString
& dn
, const PList
<ModAttrib
> & attributes
)
432 errorNumber
= ldap_modify_ext(ldapContext
,
434 CreateLDAPModArray(attributes
, ModAttrib::Replace
, storage
),
438 if (errorNumber
!= LDAP_SUCCESS
)
441 P_timeval tval
= timeout
;
442 LDAPMessage
* result
= NULL
;
443 ldap_result(ldapContext
, msgid
, LDAP_MSG_ALL
, tval
, &result
);
445 errorNumber
= ldap_result2error(ldapContext
, result
, TRUE
);
447 return errorNumber
== LDAP_SUCCESS
;
451 BOOL
PLDAPSession::Modify(const PString
& dn
, const PStringToString
& attributes
)
453 return Modify(dn
, AttribsFromDict(attributes
));
457 BOOL
PLDAPSession::Modify(const PString
& dn
, const PStringArray
& attributes
)
459 return Modify(dn
, AttribsFromArray(attributes
));
463 BOOL
PLDAPSession::Modify(const PString
& dn
, const PLDAPStructBase
& attributes
)
465 return Modify(dn
, AttribsFromStruct(attributes
));
469 BOOL
PLDAPSession::Delete(const PString
& dn
)
475 errorNumber
= ldap_delete_ext(ldapContext
, dn
, NULL
, NULL
, &msgid
);
476 if (errorNumber
!= LDAP_SUCCESS
)
479 P_timeval tval
= timeout
;
480 LDAPMessage
* result
= NULL
;
481 ldap_result(ldapContext
, msgid
, LDAP_MSG_ALL
, tval
, &result
);
483 errorNumber
= ldap_result2error(ldapContext
, result
, TRUE
);
485 return errorNumber
== LDAP_SUCCESS
;
489 PLDAPSession::SearchContext::SearchContext()
498 PLDAPSession::SearchContext::~SearchContext()
501 ldap_msgfree(message
);
503 if (result
!= NULL
&& result
!= message
)
504 ldap_msgfree(result
);
508 BOOL
PLDAPSession::Search(SearchContext
& context
,
509 const PString
& filter
,
510 const PStringArray
& attributes
,
511 const PString
& baseDN
,
518 char ** attribs
= attributes
.ToCharArray(&storage
);
520 PString base
= baseDN
;
522 base
= defaultBaseDN
;
524 static const int ScopeCode
[NumSearchScope
] = {
525 LDAP_SCOPE_BASE
, LDAP_SCOPE_ONELEVEL
, LDAP_SCOPE_SUBTREE
528 P_timeval tval
= timeout
;
530 errorNumber
= ldap_search_ext(ldapContext
,
542 if (errorNumber
!= LDAP_SUCCESS
)
545 if (ldap_result(ldapContext
, context
.msgid
, LDAP_MSG_ONE
, tval
, &context
.result
) > 0)
546 return GetNextSearchResult(context
);
549 errorNumber
= ldap_result2error(ldapContext
, context
.result
, TRUE
);
550 if (errorNumber
== 0)
551 errorNumber
= LDAP_OTHER
;
556 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
, PStringToString
& data
)
563 if (context
.result
== NULL
|| context
.message
== NULL
|| context
.completed
)
566 // Extract the resulting data
568 data
.SetAt("dn", GetSearchResultDN(context
));
570 BerElement
* ber
= NULL
;
571 char * attrib
= ldap_first_attribute(ldapContext
, context
.message
, &ber
);
572 while (attrib
!= NULL
) {
574 struct berval
** bvals
= ldap_get_values_len(ldapContext
, context
.message
, attrib
);
576 PString value
= data(attrib
);
578 for (PINDEX i
= 0; bvals
[i
] != NULL
; i
++ ) {
580 value
+= multipleValueSeparator
;
581 value
+= PString(bvals
[i
]->bv_val
, bvals
[i
]->bv_len
);
585 data
.SetAt(attrib
, value
);
588 ldap_memfree(attrib
);
589 attrib
= ldap_next_attribute(ldapContext
, context
.message
, ber
);
599 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
,
600 const PString
& attribute
,
608 if (context
.result
== NULL
|| context
.message
== NULL
|| context
.completed
)
611 if (attribute
== "dn") {
612 data
= GetSearchResultDN(context
);
616 char ** values
= ldap_get_values(ldapContext
, context
.message
, attribute
);
620 PINDEX count
= ldap_count_values(values
);
621 for (PINDEX i
= 0; i
< count
; i
++) {
623 data
+= multipleValueSeparator
;
627 ldap_value_free(values
);
632 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
,
633 const PString
& attribute
,
641 if (context
.result
== NULL
|| context
.message
== NULL
|| context
.completed
)
644 if (attribute
== "dn") {
646 data
[0] = GetSearchResultDN(context
);
650 char ** values
= ldap_get_values(ldapContext
, context
.message
, attribute
);
654 PINDEX count
= ldap_count_values(values
);
656 for (PINDEX i
= 0; i
< count
; i
++)
659 ldap_value_free(values
);
664 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
,
665 const PString
& attribute
,
666 PArray
<PBYTEArray
> & data
)
673 if (attribute
== "dn") {
674 char * dn
= ldap_get_dn(ldapContext
, context
.message
);
675 data
.Append(new PBYTEArray((const BYTE
*)dn
, ::strlen(dn
)));
680 struct berval
** values
= ldap_get_values_len(ldapContext
, context
.message
, attribute
);
684 PINDEX count
= ldap_count_values_len(values
);
686 for (PINDEX i
= 0; i
< count
; i
++)
687 data
[i
] = PBYTEArray((const BYTE
*)values
[i
]->bv_val
, values
[i
]->bv_len
);
689 ldap_value_free_len(values
);
694 BOOL
PLDAPSession::GetSearchResult(SearchContext
& context
,
695 PLDAPStructBase
& data
)
700 BOOL atLeastOne
= FALSE
;
702 for (PINDEX i
= 0; i
< data
.GetNumAttributes(); i
++) {
703 PLDAPAttributeBase
& attr
= data
.GetAttribute(i
);
704 if (attr
.IsBinary()) {
705 PArray
<PBYTEArray
> bin
;
706 if (GetSearchResult(context
, attr
.GetName(), bin
)) {
707 attr
.FromBinary(bin
);
713 if (GetSearchResult(context
, attr
.GetName(), str
)) {
714 attr
.FromString(str
);
724 PString
PLDAPSession::GetSearchResultDN(SearchContext
& context
)
728 if (context
.message
!= NULL
) {
729 char * dn
= ldap_get_dn(ldapContext
, context
.message
);
740 BOOL
PLDAPSession::GetNextSearchResult(SearchContext
& context
)
745 if (context
.result
== NULL
|| context
.completed
)
748 P_timeval tval
= timeout
;
750 if (context
.message
== NULL
)
751 context
.message
= ldap_first_message(ldapContext
, context
.result
);
753 context
.message
= ldap_next_message(ldapContext
, context
.message
);
755 if (context
.message
!= NULL
) {
756 switch (ldap_msgtype(context
.message
)) {
757 case LDAP_RES_SEARCH_ENTRY
:
758 context
.found
= TRUE
;
759 errorNumber
= LDAP_SUCCESS
;
762 case LDAP_RES_SEARCH_RESULT
:
763 errorNumber
= ldap_result2error(ldapContext
, context
.message
, FALSE
);
764 if (errorNumber
== 0 && !context
.found
)
765 errorNumber
= LDAP_NO_RESULTS_RETURNED
;
766 context
.completed
= TRUE
;
769 // Ignore other result message types for now ...
772 ldap_msgfree(context
.result
);
773 } while (ldap_result(ldapContext
, context
.msgid
, LDAP_MSG_ONE
, tval
, &context
.result
) > 0);
776 errorNumber
= ldap_result2error(ldapContext
, context
.result
, FALSE
);
777 if (errorNumber
== 0)
778 errorNumber
= LDAP_OTHER
;
783 PList
<PStringToString
> PLDAPSession::Search(const PString
& filter
,
784 const PStringArray
& attributes
,
785 const PString
& base
,
788 PList
<PStringToString
> data
;
790 SearchContext context
;
791 if (!Search(context
, filter
, attributes
, base
, scope
))
795 PStringToString
* entry
= new PStringToString
;
796 if (GetSearchResult(context
, *entry
))
802 } while (GetNextSearchResult(context
));
808 PString
PLDAPSession::GetErrorText() const
810 return ldap_err2string(errorNumber
);
814 ///////////////////////////////////////////////////////////////////////////////
816 PLDAPAttributeBase::PLDAPAttributeBase(const char * n
, void * ptr
, PINDEX sz
)
821 PLDAPStructBase::GetInitialiser().AddAttribute(this);
825 PString
PLDAPAttributeBase::ToString() const
827 PStringStream stream
;
833 void PLDAPAttributeBase::FromString(const PString
& str
)
835 PStringStream
stream(str
);
840 PBYTEArray
PLDAPAttributeBase::ToBinary() const
842 return PBYTEArray((const BYTE
*)pointer
, size
, FALSE
);
846 void PLDAPAttributeBase::FromBinary(const PArray
<PBYTEArray
> & data
)
848 if (data
.GetSize() > 0 && data
[0].GetSize() == size
)
849 memcpy(pointer
, data
[0], size
);
853 ///////////////////////////////////////////////////////////////////////////////
855 PMutex
PLDAPStructBase::initialiserMutex
;
856 PLDAPStructBase
* PLDAPStructBase::initialiserInstance
;
858 PLDAPStructBase::PLDAPStructBase()
860 attributes
.DisallowDeleteObjects();
862 initialiserMutex
.Wait();
863 initialiserStack
= initialiserInstance
;
864 initialiserInstance
= this;
868 PLDAPStructBase
& PLDAPStructBase::operator=(const PLDAPStructBase
& other
)
870 for (PINDEX i
= 0; i
< attributes
.GetSize(); i
++)
871 attributes
.GetDataAt(i
).Copy(other
.attributes
.GetDataAt(i
));
877 PLDAPStructBase
& PLDAPStructBase::operator=(const PStringArray
& array
)
879 for (PINDEX i
= 0; i
< array
.GetSize(); i
++) {
880 PString str
= array
[i
];
881 PINDEX equal
= str
.Find('=');
882 if (equal
!= P_MAX_INDEX
) {
883 PLDAPAttributeBase
* attr
= GetAttribute(str
.Left(equal
));
885 attr
->FromString(str
.Mid(equal
+1));
892 PLDAPStructBase
& PLDAPStructBase::operator=(const PStringToString
& dict
)
894 for (PINDEX i
= 0; i
< dict
.GetSize(); i
++) {
895 PLDAPAttributeBase
* attr
= GetAttribute(dict
.GetKeyAt(i
));
897 attr
->FromString(dict
.GetDataAt(i
));
903 void PLDAPStructBase::PrintOn(ostream
& strm
) const
905 strm
<< attributes
<< '\n';
909 void PLDAPStructBase::AddAttribute(PLDAPAttributeBase
* attr
)
911 attributes
.SetAt(attr
->GetName(), attr
);
915 void PLDAPStructBase::EndConstructor()
917 initialiserInstance
= initialiserStack
;
918 initialiserMutex
.Signal();
925 // End of file ////////////////////////////////////////////////////////////////