/*
- * 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 <ldap.h>
+#include <sstream>
#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 "";
+
+}
/*
- * Copyright 2000, OpenLDAP Foundation, All Rights Reserved.
+ * Copyright 2000-2006, OpenLDAP Foundation, All Rights Reserved.
* COPYING RESTRICTIONS APPLY, see COPYRIGHT file
*/
#ifndef LDAP_URL_H
#define LDAP_URL_H
-#include <ldap.h>
#include <StringList.h>
+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
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
* 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