]> git.sur5r.net Git - openldap/blobdiff - contrib/ldapc++/src/LDAPUrl.cpp
new routines for parsing/constructing LDAP Urls
[openldap] / contrib / ldapc++ / src / LDAPUrl.cpp
index e7763b4ebbdd52c516f949ff17a70805db3ba750..a960ef6f5cc481aaf91ea67d81fa28b25ecce246 100644 (file)
 /*
- * 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 "";
+
+}