Added IsSupportingRTP function to simplify detecting when STUN supports RTP
[pwlib.git] / src / ptclib / pldap.cxx
blob57ee3ddc5cec7f86e3948a718baaa878a5163914
1 /*
2 * pldap.cxx
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
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
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
35 * you cannot do this.
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.
81 #ifdef __GNUC__
82 #pragma implementation "pldap.h"
83 #endif
85 #include <ptlib.h>
87 #include <ptlib/sockets.h>
88 #include <ptclib/pldap.h>
90 #if P_LDAP
92 #include <ldap.h>
95 #if defined(_WIN32)
96 #pragma comment(lib, P_LDAP_LIBRARY)
97 #endif
99 ///////////////////////////////////////////////////////////////////////////////
101 PLDAPSession::PLDAPSession(const PString & baseDN)
102 : ldapContext(NULL),
103 errorNumber(LDAP_SUCCESS),
104 protocolVersion(LDAP_VERSION3),
105 defaultBaseDN(baseDN),
106 searchLimit(0),
107 timeout(0, 30),
108 multipleValueSeparator('\n')
113 PLDAPSession::~PLDAPSession()
115 Close();
119 BOOL PLDAPSession::Open(const PString & server, WORD port)
121 Close();
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);
131 if (!IsOpen())
132 return FALSE;
134 SetOption(LDAP_OPT_PROTOCOL_VERSION, protocolVersion);
135 return TRUE;
139 BOOL PLDAPSession::Close()
141 if (!IsOpen())
142 return FALSE;
144 ldap_unbind(ldapContext);
145 ldapContext = NULL;
146 return TRUE;
150 BOOL PLDAPSession::SetOption(int optcode, int value)
152 if (!IsOpen())
153 return FALSE;
155 return ldap_set_option(ldapContext, optcode, &value);
159 BOOL PLDAPSession::SetOption(int optcode, void * value)
161 if (!IsOpen())
162 return FALSE;
164 return ldap_set_option(ldapContext, optcode, value);
168 BOOL PLDAPSession::Bind(const PString & who,
169 const PString & passwd,
170 AuthenticationMethod authMethod)
172 if (!IsOpen())
173 return FALSE;
175 const char * whoPtr;
176 if (who.IsEmpty())
177 whoPtr = NULL;
178 else
179 whoPtr = who;
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)
190 : name(n),
191 op(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];
206 if (IsBinary())
207 mod.mod_op |= LDAP_MOD_BVALUES;
209 SetLDAPModVars(mod);
213 PLDAPSession::StringModAttrib::StringModAttrib(const PString & name,
214 Operation op)
215 : ModAttrib(name, op)
220 PLDAPSession::StringModAttrib::StringModAttrib(const PString & name,
221 const PString & value,
222 Operation op)
223 : ModAttrib(name, op)
225 AddValue(value);
229 PLDAPSession::StringModAttrib::StringModAttrib(const PString & name,
230 const PStringList & vals,
231 Operation op)
232 : ModAttrib(name, op),
233 values(vals)
238 void PLDAPSession::StringModAttrib::SetValue(const PString & value)
240 values.RemoveAll();
241 values.AppendString(value);
245 void PLDAPSession::StringModAttrib::AddValue(const PString & value)
247 values.AppendString(value);
251 BOOL PLDAPSession::StringModAttrib::IsBinary() const
253 return FALSE;
257 void PLDAPSession::StringModAttrib::SetLDAPModVars(struct ldapmod & mod)
259 pointers.SetSize(values.GetSize()+1);
260 PINDEX i;
261 for (i = 0; i < values.GetSize(); i++)
262 pointers[i] = values[i].GetPointer();
263 pointers[i] = NULL;
264 mod.mod_values = pointers.GetPointer();
268 PLDAPSession::BinaryModAttrib::BinaryModAttrib(const PString & name,
269 Operation op)
270 : ModAttrib(name, op)
275 PLDAPSession::BinaryModAttrib::BinaryModAttrib(const PString & name,
276 const PBYTEArray & value,
277 Operation op)
278 : ModAttrib(name, op)
280 AddValue(value);
284 PLDAPSession::BinaryModAttrib::BinaryModAttrib(const PString & name,
285 const PList<PBYTEArray> & vals,
286 Operation op)
287 : ModAttrib(name, op),
288 values(vals)
293 void PLDAPSession::BinaryModAttrib::SetValue(const PBYTEArray & value)
295 values.RemoveAll();
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
308 return TRUE;
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();
317 PINDEX i;
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];
323 pointers[i] = NULL;
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++) {
338 attrs[i] = &attr[i];
339 attributes[i].SetLDAPMod(attr[i], defaultOp);
342 return attrs;
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()));
354 return attrs;
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()));
370 return attrs;
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);
380 if (attr.IsBinary())
381 attrs.Append(new PLDAPSession::BinaryModAttrib(attr.GetName(), attr.ToBinary()));
382 else {
383 PString str = attr.ToString();
384 if (!str)
385 attrs.Append(new PLDAPSession::StringModAttrib(attr.GetName(), str));
389 return attrs;
393 BOOL PLDAPSession::Add(const PString & dn, const PList<ModAttrib> & attributes)
395 if (!IsOpen())
396 return FALSE;
398 PBYTEArray storage;
399 int msgid;
400 errorNumber = ldap_add_ext(ldapContext,
402 CreateLDAPModArray(attributes, ModAttrib::Add, storage),
403 NULL,
404 NULL,
405 &msgid);
406 if (errorNumber != LDAP_SUCCESS)
407 return FALSE;
409 P_timeval tval = timeout;
410 LDAPMessage * result = NULL;
411 ldap_result(ldapContext, msgid, LDAP_MSG_ALL, tval, &result);
412 if (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)
439 if (!IsOpen())
440 return FALSE;
442 PBYTEArray storage;
443 int msgid;
444 errorNumber = ldap_modify_ext(ldapContext,
446 CreateLDAPModArray(attributes, ModAttrib::Replace, storage),
447 NULL,
448 NULL,
449 &msgid);
450 if (errorNumber != LDAP_SUCCESS)
451 return FALSE;
453 P_timeval tval = timeout;
454 LDAPMessage * result = NULL;
455 ldap_result(ldapContext, msgid, LDAP_MSG_ALL, tval, &result);
456 if (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)
483 if (!IsOpen())
484 return FALSE;
486 int msgid;
487 errorNumber = ldap_delete_ext(ldapContext, dn, NULL, NULL, &msgid);
488 if (errorNumber != LDAP_SUCCESS)
489 return FALSE;
491 P_timeval tval = timeout;
492 LDAPMessage * result = NULL;
493 ldap_result(ldapContext, msgid, LDAP_MSG_ALL, tval, &result);
494 if (result)
495 errorNumber = ldap_result2error(ldapContext, result, TRUE);
497 return errorNumber == LDAP_SUCCESS;
501 PLDAPSession::SearchContext::SearchContext()
503 result = NULL;
504 message = NULL;
505 found = FALSE;
506 completed = FALSE;
510 PLDAPSession::SearchContext::~SearchContext()
512 if (message != NULL)
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,
524 SearchScope scope)
526 if (!IsOpen())
527 return FALSE;
529 PCharArray storage;
530 char ** attribs = attributes.ToCharArray(&storage);
532 PString base = baseDN;
533 if (base.IsEmpty())
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,
543 base,
544 ScopeCode[scope],
545 filter,
546 attribs,
547 FALSE,
548 NULL,
549 NULL,
550 tval,
551 searchLimit,
552 &context.msgid);
554 if (errorNumber != LDAP_SUCCESS)
555 return FALSE;
557 if (ldap_result(ldapContext, context.msgid, LDAP_MSG_ONE, tval, &context.result) > 0)
558 return GetNextSearchResult(context);
560 if (context.result)
561 errorNumber = ldap_result2error(ldapContext, context.result, TRUE);
562 if (errorNumber == 0)
563 errorNumber = LDAP_OTHER;
564 return FALSE;
568 BOOL PLDAPSession::GetSearchResult(SearchContext & context, PStringToString & data)
570 data.RemoveAll();
572 if (!IsOpen())
573 return FALSE;
575 if (context.result == NULL || context.message == NULL || context.completed)
576 return FALSE;
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);
587 if (bvals != NULL) {
588 PString value = data(attrib);
590 for (PINDEX i = 0; bvals[i] != NULL; i++ ) {
591 if (!value)
592 value += multipleValueSeparator;
593 value += PString(bvals[i]->bv_val, bvals[i]->bv_len);
595 ber_bvecfree(bvals);
597 data.SetAt(attrib, value);
600 ldap_memfree(attrib);
601 attrib = ldap_next_attribute(ldapContext, context.message, ber);
604 if (ber != NULL)
605 ber_free (ber, 0);
607 return TRUE;
611 BOOL PLDAPSession::GetSearchResult(SearchContext & context,
612 const PString & attribute,
613 PString & data)
615 data.MakeEmpty();
617 if (!IsOpen())
618 return FALSE;
620 if (context.result == NULL || context.message == NULL || context.completed)
621 return FALSE;
623 if (attribute == "dn") {
624 data = GetSearchResultDN(context);
625 return TRUE;
628 char ** values = ldap_get_values(ldapContext, context.message, attribute);
629 if (values == NULL)
630 return FALSE;
632 PINDEX count = ldap_count_values(values);
633 for (PINDEX i = 0; i < count; i++) {
634 if (!data)
635 data += multipleValueSeparator;
636 data += values[i];
639 ldap_value_free(values);
640 return TRUE;
644 BOOL PLDAPSession::GetSearchResult(SearchContext & context,
645 const PString & attribute,
646 PStringArray & data)
648 data.RemoveAll();
650 if (!IsOpen())
651 return FALSE;
653 if (context.result == NULL || context.message == NULL || context.completed)
654 return FALSE;
656 if (attribute == "dn") {
657 data.SetSize(1);
658 data[0] = GetSearchResultDN(context);
659 return TRUE;
662 char ** values = ldap_get_values(ldapContext, context.message, attribute);
663 if (values == NULL)
664 return FALSE;
666 PINDEX count = ldap_count_values(values);
667 data.SetSize(count);
668 for (PINDEX i = 0; i < count; i++)
669 data[i] = values[i];
671 ldap_value_free(values);
672 return TRUE;
676 BOOL PLDAPSession::GetSearchResult(SearchContext & context,
677 const PString & attribute,
678 PArray<PBYTEArray> & data)
680 data.RemoveAll();
682 if (!IsOpen())
683 return FALSE;
685 if (attribute == "dn") {
686 char * dn = ldap_get_dn(ldapContext, context.message);
687 data.Append(new PBYTEArray((const BYTE *)dn, ::strlen(dn)));
688 ldap_memfree(dn);
689 return TRUE;
692 struct berval ** values = ldap_get_values_len(ldapContext, context.message, attribute);
693 if (values == NULL)
694 return FALSE;
696 PINDEX count = ldap_count_values_len(values);
697 data.SetSize(count);
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);
702 return TRUE;
706 BOOL PLDAPSession::GetSearchResult(SearchContext & context,
707 PLDAPStructBase & data)
709 if (!IsOpen())
710 return FALSE;
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);
720 atLeastOne = TRUE;
723 else {
724 PString str;
725 if (GetSearchResult(context, attr.GetName(), str)) {
726 attr.FromString(str);
727 atLeastOne = TRUE;
732 return atLeastOne;
736 PString PLDAPSession::GetSearchResultDN(SearchContext & context)
738 PString str;
740 if (context.message != NULL) {
741 char * dn = ldap_get_dn(ldapContext, context.message);
742 if (dn != NULL) {
743 str = dn;
744 ldap_memfree(dn);
748 return str;
752 BOOL PLDAPSession::GetNextSearchResult(SearchContext & context)
754 if (!IsOpen())
755 return FALSE;
757 if (context.result == NULL || context.completed)
758 return FALSE;
760 P_timeval tval = timeout;
761 do {
762 if (context.message == NULL)
763 context.message = ldap_first_message(ldapContext, context.result);
764 else
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;
772 return TRUE;
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;
779 return FALSE;
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);
787 if (context.result)
788 errorNumber = ldap_result2error(ldapContext, context.result, FALSE);
789 if (errorNumber == 0)
790 errorNumber = LDAP_OTHER;
791 return FALSE;
795 PList<PStringToString> PLDAPSession::Search(const PString & filter,
796 const PStringArray & attributes,
797 const PString & base,
798 SearchScope scope)
800 PList<PStringToString> data;
802 SearchContext context;
803 if (!Search(context, filter, attributes, base, scope))
804 return data;
806 do {
807 PStringToString * entry = new PStringToString;
808 if (GetSearchResult(context, *entry))
809 data.Append(entry);
810 else {
811 delete entry;
812 break;
814 } while (GetNextSearchResult(context));
816 return data;
820 PString PLDAPSession::GetErrorText() const
822 return ldap_err2string(errorNumber);
826 ///////////////////////////////////////////////////////////////////////////////
828 PLDAPAttributeBase::PLDAPAttributeBase(const char * n, void * ptr, PINDEX sz)
829 : name(n),
830 pointer(ptr),
831 size(sz)
833 PLDAPStructBase::GetInitialiser().AddAttribute(this);
837 PString PLDAPAttributeBase::ToString() const
839 PStringStream stream;
840 PrintOn(stream);
841 return stream;
845 void PLDAPAttributeBase::FromString(const PString & str)
847 PStringStream stream(str);
848 ReadFrom(stream);
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));
885 return *this;
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));
896 if (attr != NULL)
897 attr->FromString(str.Mid(equal+1));
900 return *this;
904 PLDAPStructBase & PLDAPStructBase::operator=(const PStringToString & dict)
906 for (PINDEX i = 0; i < dict.GetSize(); i++) {
907 PLDAPAttributeBase * attr = GetAttribute(dict.GetKeyAt(i));
908 if (attr != NULL)
909 attr->FromString(dict.GetDataAt(i));
911 return *this;
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();
934 #endif // P_LDAP
937 // End of file ////////////////////////////////////////////////////////////////