File indexing completed on 2024-12-15 04:50:15

0001 /*
0002   This file is part of libkldap.
0003   SPDX-FileCopyrightText: 2006 Sean Harmer <sh@theharmers.co.uk>
0004 
0005   SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "ldapdn.h"
0009 
0010 #include "ldap_core_debug.h"
0011 #include <algorithm>
0012 
0013 using namespace KLDAPCore;
0014 
0015 class Q_DECL_HIDDEN LdapDN::LdapDNPrivate
0016 {
0017 public:
0018     LdapDNPrivate() = default;
0019 
0020     ~LdapDNPrivate() = default;
0021 
0022     [[nodiscard]] bool isValidRDNString(const QString &rdn) const;
0023     [[nodiscard]] QStringList splitOnNonEscapedChar(const QString &rdn, QChar ch) const;
0024 
0025     QString m_dn;
0026 };
0027 
0028 bool LdapDN::LdapDNPrivate::isValidRDNString(const QString &rdn) const
0029 {
0030     qCDebug(LDAP_LOG) << "Testing rdn:" << rdn;
0031 
0032     // If it is a muli-valued rdn, split it into its constituent parts
0033     const QStringList rdnParts = splitOnNonEscapedChar(rdn, QLatin1Char('+'));
0034     const int rdnPartsSize(rdnParts.size());
0035     if (rdnPartsSize > 1) {
0036         for (int i = 0; i < rdnPartsSize; i++) {
0037             if (!isValidRDNString(rdnParts.at(i))) {
0038                 return false;
0039             }
0040         }
0041         return true;
0042     }
0043     // Split the rdn into the attribute name and value parts
0044     const auto components = QStringView(rdn).split(QLatin1Char('='));
0045     // We should have exactly two parts
0046     if (components.size() != 2) {
0047         return false;
0048     }
0049 
0050     return true;
0051 }
0052 
0053 QStringList LdapDN::LdapDNPrivate::splitOnNonEscapedChar(const QString &str, QChar ch) const
0054 {
0055     QStringList strParts;
0056     int index = 0;
0057     int searchFrom = 0;
0058     int strPartStartIndex = 0;
0059     while ((index = str.indexOf(ch, searchFrom)) != -1) {
0060         const QChar prev = str[std::max(0, index - 1)];
0061         if (prev != QLatin1Char('\\')) {
0062             // Found a component of a multi-valued RDN
0063             // qCDebug(LDAP_LOG) << "Found" << ch << "at index" << index;
0064             QString tmp = str.mid(strPartStartIndex, index - strPartStartIndex);
0065             // qCDebug(LDAP_LOG) << "Adding part:" << tmp;
0066             strParts.append(tmp);
0067             strPartStartIndex = index + 1;
0068         }
0069 
0070         searchFrom = index + 1;
0071     }
0072 
0073     // Add on the part after the last found delimiter
0074     QString tmp = str.mid(strPartStartIndex);
0075     // qCDebug(LDAP_LOG) << "Adding part:" << tmp;
0076     strParts.append(tmp);
0077 
0078     return strParts;
0079 }
0080 
0081 LdapDN::LdapDN()
0082     : d(new LdapDNPrivate)
0083 {
0084 }
0085 
0086 LdapDN::LdapDN(const QString &dn)
0087     : d(new LdapDNPrivate)
0088 {
0089     d->m_dn = dn;
0090 }
0091 
0092 LdapDN::LdapDN(const LdapDN &that)
0093     : d(new LdapDNPrivate)
0094 {
0095     *d = *that.d;
0096 }
0097 
0098 LdapDN &LdapDN::operator=(const LdapDN &that)
0099 {
0100     if (this == &that) {
0101         return *this;
0102     }
0103 
0104     *d = *that.d;
0105     return *this;
0106 }
0107 
0108 LdapDN::~LdapDN() = default;
0109 
0110 void LdapDN::clear()
0111 {
0112     d->m_dn.clear();
0113 }
0114 
0115 bool LdapDN::isEmpty() const
0116 {
0117     return d->m_dn.isEmpty();
0118 }
0119 
0120 QString LdapDN::toString() const
0121 {
0122     return d->m_dn;
0123 }
0124 
0125 QString LdapDN::toString(int depth) const
0126 {
0127     const QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(','));
0128     if (depth >= rdns.size()) {
0129         return {};
0130     }
0131 
0132     // Construct a DN down to the requested depth
0133     QString dn;
0134     for (int i = depth; i >= 0; i--) {
0135         dn += rdns.at(rdns.size() - 1 - i) + QLatin1Char(',');
0136         qCDebug(LDAP_LOG) << "dn =" << dn;
0137     }
0138     dn.chop(1); // Strip off the extraneous comma
0139 
0140     return dn;
0141 }
0142 
0143 QString LdapDN::rdnString() const
0144 {
0145     /** \TODO We should move this into the d pointer as we calculate rdns quite a lot */
0146     const QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(','));
0147     return rdns.at(0);
0148 }
0149 
0150 QString LdapDN::rdnString(int depth) const
0151 {
0152     const QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(','));
0153     if (depth >= rdns.size()) {
0154         return {};
0155     }
0156     return rdns.at(rdns.size() - 1 - depth);
0157 }
0158 
0159 bool LdapDN::isValid() const
0160 {
0161     qCDebug(LDAP_LOG) << "Testing dn:" << d->m_dn;
0162 
0163     // Break the string into rdn's
0164     const QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(','));
0165 
0166     // Test to see if each rdn is valid
0167     const int rdnsSize(rdns.size());
0168     for (int i = 0; i < rdnsSize; i++) {
0169         if (!d->isValidRDNString(rdns.at(i))) {
0170             return false;
0171         }
0172     }
0173 
0174     return true;
0175 }
0176 
0177 int LdapDN::depth() const
0178 {
0179     const QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(','));
0180     return rdns.size();
0181 }
0182 
0183 bool LdapDN::operator==(const LdapDN &rhs) const
0184 {
0185     return d->m_dn == rhs.d->m_dn;
0186 }
0187 
0188 bool LdapDN::operator!=(const LdapDN &rhs) const
0189 {
0190     return d->m_dn != rhs.d->m_dn;
0191 }