From 804ff1ed946192f6da9e086c2adc0eca5741b7a9 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Mon, 3 Jul 2006 16:30:33 +0000 Subject: [PATCH] new routines for parsing/constructing LDAP Urls --- contrib/ldapc++/src/LDAPUrl.cpp | 487 +++++++++++++++++++++++++++++--- contrib/ldapc++/src/LDAPUrl.h | 136 ++++++++- 2 files changed, 583 insertions(+), 40 deletions(-) diff --git a/contrib/ldapc++/src/LDAPUrl.cpp b/contrib/ldapc++/src/LDAPUrl.cpp index e7763b4ebb..a960ef6f5c 100644 --- a/contrib/ldapc++/src/LDAPUrl.cpp +++ b/contrib/ldapc++/src/LDAPUrl.cpp @@ -1,74 +1,495 @@ /* - * Copyright 2000, OpenLDAP Foundation, All Rights Reserved. + * Copyright 2000-2006, OpenLDAP Foundation, All Rights Reserved. * COPYING RESTRICTIONS APPLY, see COPYRIGHT file */ #include "LDAPUrl.h" - -#include +#include #include "debug.h" using namespace std; -LDAPUrl::LDAPUrl(const char *url){ +#define PCT_ENCFLAG_NONE 0x0000U +#define PCT_ENCFLAG_COMMA 0x0001U +#define PCT_ENCFLAG_SLASH 0x0002U + +#define LDAP_DEFAULT_PORT 389 +#define LDAPS_DEFAULT_PORT 636 + +LDAPUrl::LDAPUrl(const std::string &url) +{ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPUrl::LDAPUrl()" << endl); DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER, " url:" << url << endl); - if (ldap_is_ldap_url(url)){ - LDAPURLDesc *urlDesc; - ldap_url_parse(url, &urlDesc); - if(urlDesc->lud_host){ - m_Host = string(urlDesc->lud_host); - } - m_Port = urlDesc->lud_port; - if(urlDesc->lud_dn){ - m_DN = string(urlDesc->lud_dn); - } - m_Attrs = StringList(urlDesc->lud_attrs); - m_Scope = urlDesc->lud_scope; - if(urlDesc->lud_filter){ - m_Filter = string(urlDesc->lud_filter); - }else{ - m_Filter = ""; - } - m_urlString= string(url); - ldap_free_urldesc(urlDesc); - }else{ - DEBUG(LDAP_DEBUG_TRACE," noUrl:" << url << endl); + m_urlString = url; + m_Filter = ""; + m_Scheme = "ldap"; + m_Scope = 0; + m_Port = 0; + regenerate = false; + if (url != "") { + this->parseUrl(); } } -LDAPUrl::~LDAPUrl(){ +LDAPUrl::~LDAPUrl() +{ DEBUG(LDAP_DEBUG_DESTROY, "LDAPUrl::~LDAPUrl()" << endl); m_Attrs.clear(); } -int LDAPUrl::getPort() const { +int LDAPUrl::getPort() const +{ return m_Port; } -int LDAPUrl::getScope() const { +void LDAPUrl::setPort(int port) +{ + m_Port = port; + regenerate = true; +} + +int LDAPUrl::getScope() const +{ return m_Scope; } -const string& LDAPUrl::getURLString() const { +void LDAPUrl::setScope( const std::string &scope ) +{ + if (scope == "base" || scope == "" ) { + m_Scope = 0; + } else if (scope == "one" ) { + m_Scope = 1; + } else if (scope == "sub" ) { + m_Scope = 2; + } else { + throw LDAPUrlException(LDAPUrlException::INVALID_SCOPE, + "Scope was:" + scope); + } + regenerate = true; +} + +const string& LDAPUrl::getURLString() +{ + if (regenerate){ + this->components2Url(); + regenerate=false; + } return m_urlString; } -const string& LDAPUrl::getHost() const { +void LDAPUrl::setURLString( const std::string &url ) +{ + m_urlString = url; + if (url != "") { + this->parseUrl(); + } + regenerate = false; +} + +const string& LDAPUrl::getHost() const +{ return m_Host; } -const string& LDAPUrl::getDN() const { +void LDAPUrl::setHost( const std::string &host ) +{ + m_Host = host; + regenerate = true; +} + +const string& LDAPUrl::getDN() const +{ return m_DN; } +void LDAPUrl::setDN( const std::string &dn ) +{ + m_DN = dn; + regenerate = true; +} -const string& LDAPUrl::getFilter() const { +const string& LDAPUrl::getFilter() const +{ return m_Filter; } +void LDAPUrl::setFilter( const std::string &filter ) +{ + m_Filter = filter; + regenerate = true; +} -const StringList& LDAPUrl::getAttrs() const { +const StringList& LDAPUrl::getAttrs() const +{ return m_Attrs; } +void LDAPUrl::setAttrs( const StringList &attrs ) +{ + m_Attrs = attrs; + regenerate = true; +} + +const StringList& LDAPUrl::getExtensions() const +{ + return m_Extensions; +} + +void LDAPUrl::setExtensions( const StringList &ext ) +{ + m_Extensions = ext; + regenerate = true; +} + +const std::string& LDAPUrl::getScheme() const +{ + return m_Scheme; +} + +void LDAPUrl::setScheme( const std::string &scheme ) +{ + if (scheme == "ldap" || scheme == "ldaps" || + scheme == "ldapi" || scheme == "cldap" ) + { + m_Scheme = scheme; + regenerate = true; + } else { + throw LDAPUrlException(LDAPUrlException::INVALID_SCHEME, + "Unknown URL scheme: \"" + scheme + "\""); + } +} + +void LDAPUrl::parseUrl() +{ + DEBUG(LDAP_DEBUG_TRACE, "LDAPUrl::parseUrl()" << std::endl); + // reading Scheme + std::string::size_type pos = m_urlString.find(':'); + std::string::size_type startpos = m_urlString.find(':'); + if (pos == std::string::npos) { + throw LDAPUrlException(LDAPUrlException::INVALID_URL, + "No colon found in URL"); + } + std::string scheme = m_urlString.substr(0, pos); + DEBUG(LDAP_DEBUG_TRACE, " scheme is <" << scheme << ">" << std::endl); + + if ( scheme == "ldap" ) { + m_Scheme = scheme; + } else if ( scheme == "ldaps" ) { + m_Scheme = scheme; + } else if ( scheme == "ldapi" ) { + m_Scheme = scheme; + } else if ( scheme == "cldap" ) { + m_Scheme = scheme; + } else { + throw LDAPUrlException(LDAPUrlException::INVALID_SCHEME, + "Unknown URL Scheme: \"" + scheme + "\""); + } + + if ( m_urlString[pos+1] != '/' || m_urlString[pos+2] != '/' ) { + throw LDAPUrlException(LDAPUrlException::INVALID_URL); + } else { + startpos = pos + 3; + } + if ( m_urlString[startpos] == '/' ) { + startpos++; + } else { + pos = m_urlString.find('/', startpos); + std::string hostport = m_urlString.substr(startpos, + pos - startpos); + DEBUG(LDAP_DEBUG_TRACE, " hostport: <" << hostport << ">" + << std::endl); + std::string::size_type portstart = m_urlString.find(':', startpos); + if (portstart == std::string::npos || portstart > pos ) { + percentDecode(hostport, m_Host); + if ( m_Scheme == "ldap" || m_Scheme == "cldap" ) { + m_Port = LDAP_DEFAULT_PORT; + } else if ( m_Scheme == "ldaps" ) { + m_Port = LDAPS_DEFAULT_PORT; + } + } else { + std::string tmp = m_urlString.substr(startpos, + portstart - startpos); + percentDecode(tmp, m_Host); + DEBUG(LDAP_DEBUG_TRACE, "Host: <" << m_Host << ">" << std::endl); + std::string port = m_urlString.substr(portstart+1, + pos-portstart-1); + if ( port.length() > 0 ) { + std::istringstream i(port); + i >> m_Port; + if ( i.fail() ){ + throw LDAPUrlException(LDAPUrlException::INVALID_PORT); + } + } + DEBUG(LDAP_DEBUG_TRACE, " Port: <" << m_Port << ">" + << std::endl); + } + } + startpos = pos + 1; + int parserMode = base; + while ( pos != std::string::npos ) { + pos = m_urlString.find('?', startpos); + std::string actComponent = m_urlString.substr(startpos, + pos - startpos); + DEBUG(LDAP_DEBUG_TRACE, " ParserMode:" << parserMode << std::endl); + DEBUG(LDAP_DEBUG_TRACE, " ActComponent: <" << actComponent << ">" + << std::endl); + std::string s_scope = ""; + std::string s_ext = ""; + switch(parserMode) { + case base : + percentDecode(actComponent, m_DN); + DEBUG(LDAP_DEBUG_TRACE, " BaseDN:" << m_DN << std::endl); + break; + case attrs : + DEBUG(LDAP_DEBUG_TRACE, " reading Attributes" << std::endl); + if (actComponent.length() != 0 ) { + string2list(actComponent,m_Attrs, true); + } + break; + case scope : + percentDecode(actComponent, s_scope); + if (s_scope == "base" || s_scope == "" ) { + m_Scope = 0; + } else if (s_scope == "one" ) { + m_Scope = 1; + } else if (s_scope == "sub" ) { + m_Scope = 2; + } else { + throw LDAPUrlException(LDAPUrlException::INVALID_SCOPE); + } + DEBUG(LDAP_DEBUG_TRACE, " Scope: <" << s_scope << ">" + << std::endl); + break; + case filter : + percentDecode(actComponent, m_Filter); + DEBUG(LDAP_DEBUG_TRACE, " filter: <" << m_Filter << ">" + << std::endl); + break; + case extensions : + DEBUG(LDAP_DEBUG_TRACE, " reading Extensions" << std::endl); + string2list(actComponent, m_Extensions, true); + break; + default : + DEBUG(LDAP_DEBUG_TRACE, " unknown state" << std::endl); + break; + } + startpos = pos + 1; + parserMode++; + } +} + +void LDAPUrl::percentDecode(const std::string& src, std::string &out) +{ + DEBUG(LDAP_DEBUG_TRACE, "LDAPUrl::percentDecode()" << std::endl); + std::string::size_type pos = 0; + std::string::size_type startpos = 0; + pos = src.find('%', startpos); + while ( pos != std::string::npos ) { + out += src.substr(startpos, pos - startpos); + std::string istr(src.substr(pos+1, 2)); + std::istringstream i(istr); + i.setf(std::ios::hex, std::ios::basefield); + i.unsetf(std::ios::showbase); + int hex; + i >> hex; + if ( i.fail() ){ + throw LDAPUrlException(LDAPUrlException::URL_DECODING_ERROR, + "Invalid percent encoding"); + } + char j = hex; + out.push_back(j); + startpos = pos+3; + pos = src.find('%', startpos); + } + out += src.substr(startpos, pos - startpos); +} + +void LDAPUrl::string2list(const std::string &src, StringList& sl, + bool percentDecode) +{ + std::string::size_type comma_startpos = 0; + std::string::size_type comma_pos = 0; + std::string actItem; + while ( comma_pos != std::string::npos ) { + comma_pos = src.find(',', comma_startpos); + actItem = src.substr(comma_startpos, comma_pos - comma_startpos); + if (percentDecode){ + std::string decoded; + this->percentDecode(actItem,decoded); + actItem = decoded; + } + sl.add(actItem); + comma_startpos = comma_pos + 1; + } +} + + +void LDAPUrl::components2Url() +{ + std::ostringstream url; + std::string encoded = ""; + this->percentEncode(m_Host, encoded, PCT_ENCFLAG_SLASH); + url << m_Scheme << "://" << encoded; + if ( m_Port != 0 ) { + url << ":" << m_Port; + } + + url << "/"; + encoded = ""; + if ( m_DN != "" ) { + this->percentEncode( m_DN, encoded ); + url << encoded; + } + string qm = ""; + if ( ! m_Attrs.empty() ){ + url << "?"; + bool first = true; + for ( StringList::const_iterator i = m_Attrs.begin(); + i != m_Attrs.end(); i++) + { + this->percentEncode( *i, encoded ); + if ( ! first ) { + url << ","; + } else { + first = false; + } + url << encoded; + } + } else { + qm.append("?"); + } + if ( m_Scope == 1 ) { + url << qm << "?one"; + qm = ""; + } else if ( m_Scope == 2 ) { + url << qm << "?sub"; + qm = ""; + } else { + qm.append("?"); + } + if (m_Filter != "" ){ + this->percentEncode( m_Filter, encoded ); + url << qm << "?" << encoded; + qm = ""; + } else { + qm.append("?"); + } + + if ( ! m_Extensions.empty() ){ + url << qm << "?"; + bool first = true; + for ( StringList::const_iterator i = m_Extensions.begin(); + i != m_Extensions.end(); i++) + { + this->percentEncode( *i, encoded, 1); + if ( ! first ) { + url << ","; + } else { + first = false; + } + url << encoded; + } + } + m_urlString=url.str(); +} + + +void LDAPUrl::percentEncode( const std::string &src, + std::string &dest, + int flags) +{ + std::ostringstream o; + o.setf(std::ios::hex, std::ios::basefield); + o.setf(std::ios::uppercase); + o.unsetf(std::ios::showbase); + bool escape=false; + for ( std::string::const_iterator i = src.begin(); i != src.end(); i++ ){ + switch(*i){ + /* reserved */ + case '?' : + escape = true; + break; + case ',' : + if ( flags & PCT_ENCFLAG_COMMA ) { + escape = true; + } else { + escape = false; + } + break; + case ':' : + case '/' : + if ( flags & PCT_ENCFLAG_SLASH ) { + escape = true; + } else { + escape = false; + } + break; + case '#' : + case '[' : + case ']' : + case '@' : + case '!' : + case '$' : + case '&' : + case '\'' : + case '(' : + case ')' : + case '*' : + case '+' : + case ';' : + case '=' : + /* unreserved */ + case '-' : + case '.' : + case '_' : + case '~' : + escape = false; + break; + default : + if ( std::isalnum(*i) ) { + escape = false; + } else { + escape = true; + } + break; + } + if ( escape ) { + o << "%" << (int)(unsigned char)*i ; + } else { + o.put(*i); + } + } + dest = o.str(); +} + +const code2string_s LDAPUrlException::code2string[] = { + { INVALID_SCHEME, "Invalid URL Scheme" }, + { INVALID_PORT, "Invalid Port in Url" }, + { INVALID_SCOPE, "Invalid Search Scope in Url" }, + { INVALID_URL, "Invalid LDAP Url" }, + { URL_DECODING_ERROR, "Url-decoding Error" }, + { 0, 0 } +}; +LDAPUrlException::LDAPUrlException( int code, const std::string &msg) : + m_code(code), m_addMsg(msg) {} + +int LDAPUrlException::getCode() const +{ + return m_code; +} + +const std::string LDAPUrlException::getAdditionalInfo() const +{ + return m_addMsg; +} + +const std::string LDAPUrlException::getErrorMessage() const +{ + for ( int i = 0; code2string[i].string != 0; i++ ) { + if ( code2string[i].code == m_code ) { + return std::string(code2string[i].string); + } + } + return ""; + +} diff --git a/contrib/ldapc++/src/LDAPUrl.h b/contrib/ldapc++/src/LDAPUrl.h index 9314aa09b2..16e1810e6f 100644 --- a/contrib/ldapc++/src/LDAPUrl.h +++ b/contrib/ldapc++/src/LDAPUrl.h @@ -1,5 +1,5 @@ /* - * Copyright 2000, OpenLDAP Foundation, All Rights Reserved. + * Copyright 2000-2006, OpenLDAP Foundation, All Rights Reserved. * COPYING RESTRICTIONS APPLY, see COPYRIGHT file */ @@ -7,9 +7,9 @@ #ifndef LDAP_URL_H #define LDAP_URL_H -#include #include +class LDAPUrlException; /** * This class is used to analyze and store LDAP-Urls as returned by a * LDAP-Server as Referrals and Search References. LDAP-URLs are defined @@ -22,9 +22,10 @@ class LDAPUrl{ public : /** - * Create a new object from a c-string that contains a LDAP-Url + * Create a new object from a string that contains a LDAP-Url + * @param url The URL String */ - LDAPUrl(const char *url); + LDAPUrl(const std::string &url=""); /** * Destructor @@ -36,47 +37,168 @@ class LDAPUrl{ * port */ int getPort() const; + + /** + * Set the port value of the URL + * @param dn The port value + */ + void setPort(int port); /** * @return The scope part of the URL is returned. */ int getScope() const; + /** + * Set the Scope part of the URL + * @param scope The new scope + */ + void setScope(const std::string& scope); + /** * @return The complete URL as a string */ - const std::string& getURLString() const; + const std::string& getURLString(); + + /** + * Set the URL member attribute + * @param url The URL String + */ + void setURLString(const std::string &url); /** * @return The hostname or IP-Address of the destination host. */ const std::string& getHost() const; + /** + * Set the Host part of the URL + * @param host The new host part + */ + void setHost( const std::string &host); + + /** + * @return The Protocol Scheme of the URL. + */ + const std::string& getScheme() const; + + /** + * Set the Protocol Scheme of the URL + * @param host The Protcol scheme. Allowed values are + * ldap,ldapi,ldaps and cldap + */ + void setScheme( const std::string &scheme ); + /** * @return The Base-DN part of the URL */ const std::string& getDN() const; + + /** + * Set the DN part of the URL + * @param dn The new DN part + */ + void setDN( const std::string &dn); /** * @return The Filter part of the URL */ const std::string& getFilter() const; + + /** + * Set the Filter part of the URL + * @param filter The new Filter + */ + void setFilter( const std::string &filter); /** * @return The List of attributes that was in the URL */ const StringList& getAttrs() const; - + + /** + * Set the Attributes part of the URL + * @param attrs StringList constaining the List of Attributes + */ + void setAttrs( const StringList &attrs); + void setExtensions( const StringList &ext); + const StringList& getExtensions() const; + + /** + * Percent-decode a string + * @param src The string that is to be decoded + * @param dest The decoded result string + */ + void percentDecode( const std::string& src, std::string& dest ); + + /** + * Percent-encoded a string + * @param src The string that is to be encoded + * @param dest The encoded result string + * @param flags + */ + void percentEncode( const std::string& src, + std::string& dest, + int flags=0 ); + + protected : + /** + * Split the url string that is associated with this Object into + * it components. The compontens of the URL can be access via the + * get...() methods. + * (this function is mostly for internal use and gets called + * automatically whenever necessary) + */ + void parseUrl(); + + /** + * Generate an URL string from the components that were set with + * the various set...() methods + * (this function is mostly for internal use and gets called + * automatically whenever necessary) + */ + void components2Url(); + + void string2list(const std::string &src, StringList& sl, + bool percentDecode=false); + protected : + bool regenerate; int m_Port; int m_Scope; std::string m_Host; std::string m_DN; std::string m_Filter; StringList m_Attrs; - LDAPURLDesc *m_urlDesc; + StringList m_Extensions; std::string m_urlString; + std::string m_Scheme; + enum mode { base, attrs, scope, filter, extensions }; }; +struct code2string_s { + int code; + const char* string; +}; + +class LDAPUrlException { + public : + LDAPUrlException(int code, const std::string &msg="" ); + + int getCode() const; + const std::string getErrorMessage() const; + const std::string getAdditionalInfo() const; + + static const int INVALID_SCHEME = 1; + static const int INVALID_PORT = 2; + static const int INVALID_SCOPE = 3; + static const int INVALID_URL = 4; + static const int URL_DECODING_ERROR = 5; + static const code2string_s code2string[]; + + private: + int m_code; + std::string m_addMsg; +}; #endif //LDAP_URL_H -- 2.39.5