1/*
2 This file is part of libkldap.
3 Copyright (c) 2006 Sean Harmer <sh@theharmers.co.uk>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21#include "ldapdn.h"
22
23#include <algorithm>
24
25#include <kdebug.h>
26
27using namespace KLDAP;
28
29class LdapDN::LdapDNPrivate
30{
31 public:
32 LdapDNPrivate() : m_dn() {}
33 ~LdapDNPrivate() {}
34
35 bool isValidRDNString( const QString &rdn ) const;
36 QStringList splitOnNonEscapedChar( const QString &rdn, const QChar &ch ) const;
37
38 QString m_dn;
39};
40
41bool LdapDN::LdapDNPrivate::isValidRDNString( const QString &rdn ) const
42{
43 kDebug() << "Testing rdn:" << rdn;
44
45 // If it is a muli-valued rdn, split it into its constituent parts
46 const QStringList rdnParts = splitOnNonEscapedChar( rdn, QLatin1Char( '+' ) );
47 const int rdnPartsSize( rdnParts.size() );
48 if ( rdnPartsSize > 1 ) {
49 for ( int i = 0; i < rdnPartsSize; i++ ) {
50 if ( !isValidRDNString( rdnParts.at( i ) ) ) {
51 return false;
52 }
53 }
54 return true;
55 }
56
57 // Split the rdn into the attribute name and value parts
58 QStringList components = rdn.split( QLatin1Char('=') );
59
60 // We should have exactly two parts
61 if ( components.size() != 2 ) {
62 return false;
63 }
64
65 return true;
66}
67
68QStringList LdapDN::LdapDNPrivate::splitOnNonEscapedChar( const QString &str,
69 const QChar &ch ) const
70{
71 QStringList strParts;
72 int index = 0;
73 int searchFrom = 0;
74 int strPartStartIndex = 0;
75 while ( ( index = str.indexOf( ch, searchFrom ) ) != -1 ) {
76 const QChar prev = str[std::max( 0, index - 1 )];
77 if ( prev != QLatin1Char( '\\' ) ) {
78 // Found a component of a multi-valued RDN
79 //kDebug() << "Found" << ch << "at index" << index;
80 QString tmp = str.mid( strPartStartIndex, index - strPartStartIndex );
81 //kDebug() << "Adding part:" << tmp;
82 strParts.append( tmp );
83 strPartStartIndex = index + 1;
84 }
85
86 searchFrom = index + 1;
87 }
88
89 // Add on the part after the last found delimeter
90 QString tmp = str.mid( strPartStartIndex );
91 //kDebug() << "Adding part:" << tmp;
92 strParts.append( tmp );
93
94 return strParts;
95}
96
97LdapDN::LdapDN()
98 : d( new LdapDNPrivate )
99{
100
101}
102
103LdapDN::LdapDN( const QString &dn )
104 : d( new LdapDNPrivate )
105{
106 d->m_dn = dn;
107}
108
109LdapDN::LdapDN( const LdapDN &that )
110 : d( new LdapDNPrivate )
111{
112 *d = *that.d;
113}
114
115LdapDN &LdapDN::operator=( const LdapDN &that )
116{
117 if ( this == &that ) {
118 return *this;
119 }
120
121 *d = *that.d;
122 return *this;
123}
124
125LdapDN::~LdapDN()
126{
127 delete d;
128}
129
130void LdapDN::clear()
131{
132 d->m_dn.clear();
133}
134
135bool LdapDN::isEmpty() const
136{
137 return d->m_dn.isEmpty();
138}
139
140QString LdapDN::toString() const
141{
142 return d->m_dn;
143}
144
145QString LdapDN::toString( int depth ) const
146{
147 QStringList rdns = d->splitOnNonEscapedChar( d->m_dn, QLatin1Char( ',' ) );
148 if ( depth >= rdns.size() ) {
149 return QString();
150 }
151
152 // Construct a DN down to the requested depth
153 QString dn;
154 for ( int i = depth; i >= 0; i-- ) {
155 dn += rdns.at( rdns.size() - 1 - i ) + QLatin1Char( ',' );
156 kDebug() << "dn =" << dn;
157 }
158 dn = dn.left( dn.length() - 1 ); // Strip off the extraneous comma
159
160 return dn;
161}
162
163QString LdapDN::rdnString() const
164{
165 /** \TODO We should move this into the d pointer as we calculate rdns quite a lot */
166 QStringList rdns = d->splitOnNonEscapedChar( d->m_dn, QLatin1Char( ',' ) );
167 return rdns.at( 0 );
168}
169
170QString LdapDN::rdnString( int depth ) const
171{
172 QStringList rdns = d->splitOnNonEscapedChar( d->m_dn, QLatin1Char( ',' ) );
173 if ( depth >= rdns.size() ) {
174 return QString();
175 }
176 return rdns.at( rdns.size() - 1 - depth );
177}
178
179bool LdapDN::isValid() const
180{
181 kDebug() << "Testing dn:" << d->m_dn;
182
183 // Break the string into rdn's
184 const QStringList rdns = d->splitOnNonEscapedChar( d->m_dn, QLatin1Char( ',' ) );
185
186 // Test to see if each rdn is valid
187 const int rdnsSize( rdns.size() );
188 for ( int i = 0; i < rdnsSize; i++ ) {
189 if ( !d->isValidRDNString( rdns.at( i ) ) ) {
190 return false;
191 }
192 }
193
194 return true;
195}
196
197int LdapDN::depth() const
198{
199 QStringList rdns = d->splitOnNonEscapedChar( d->m_dn, QLatin1Char( ',' ) );
200 return rdns.size();
201}
202
203bool LdapDN::operator == ( const LdapDN &rhs ) const
204{
205 return d->m_dn == rhs.d->m_dn;
206}
207
208bool LdapDN::operator != ( const LdapDN &rhs ) const
209{
210 return d->m_dn != rhs.d->m_dn;
211}
212