Back to index

openldap  2.4.31
LDAPUrl.cpp
Go to the documentation of this file.
00001 // $OpenLDAP$
00002 /*
00003  * Copyright 2000-2012 The OpenLDAP Foundation, All Rights Reserved.
00004  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
00005  */
00006 
00007 
00008 #include "LDAPUrl.h"
00009 #include <sstream>
00010 #include <iomanip>
00011 #include "debug.h"
00012 
00013 using namespace std;
00014 
00015 #define PCT_ENCFLAG_NONE 0x0000U
00016 #define PCT_ENCFLAG_COMMA 0x0001U
00017 #define PCT_ENCFLAG_SLASH 0x0002U
00018 
00019 #define LDAP_DEFAULT_PORT 389
00020 #define LDAPS_DEFAULT_PORT 636
00021 
00022 LDAPUrl::LDAPUrl(const std::string &url)
00023 {
00024     DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPUrl::LDAPUrl()" << endl);
00025     DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
00026             "   url:" << url << endl);
00027     m_urlString = url;
00028     m_Filter = "";
00029     m_Scheme = "ldap";
00030     m_Scope = 0;
00031     m_Port = 0;
00032     regenerate = false;
00033     if (url != "") {
00034         this->parseUrl();
00035     }
00036 }
00037 
00038 LDAPUrl::~LDAPUrl()
00039 {
00040     DEBUG(LDAP_DEBUG_DESTROY, "LDAPUrl::~LDAPUrl()" << endl);
00041     m_Attrs.clear();
00042 }
00043 
00044 int LDAPUrl::getPort() const 
00045 {
00046     return m_Port;
00047 }
00048 
00049 void LDAPUrl::setPort(int port)
00050 {
00051     m_Port = port;
00052     regenerate = true;
00053 }
00054 
00055 int LDAPUrl::getScope() const 
00056 {
00057     return m_Scope;
00058 }
00059 
00060 void LDAPUrl::setScope( const std::string &scope )
00061 {
00062     if (scope == "base" || scope == "" ) {
00063         m_Scope = 0;
00064     } else if (scope == "one" ) {
00065         m_Scope = 1;
00066     } else if (scope == "sub" ) {
00067         m_Scope = 2;
00068     } else {
00069         throw LDAPUrlException(LDAPUrlException::INVALID_SCOPE, 
00070                 "Scope was:" + scope); 
00071     }
00072     regenerate = true;
00073 }
00074 
00075 const string& LDAPUrl::getURLString() const
00076 {
00077     if (regenerate){
00078         this->components2Url();
00079         regenerate=false;
00080     }
00081     return m_urlString;
00082 }
00083 
00084 void LDAPUrl::setURLString( const std::string &url )
00085 {
00086     m_urlString = url;
00087     if (url != "") {
00088         this->parseUrl();
00089     }
00090     regenerate = false;
00091 }
00092 
00093 const string& LDAPUrl::getHost() const 
00094 {
00095     return m_Host;
00096 }
00097 
00098 void LDAPUrl::setHost( const std::string &host )
00099 {
00100     m_Host = host;
00101     regenerate = true;
00102 }
00103 
00104 const string& LDAPUrl::getDN() const 
00105 {
00106     return m_DN;
00107 }
00108 void LDAPUrl::setDN( const std::string &dn )
00109 {
00110     m_DN = dn;
00111     regenerate = true;
00112 }
00113 
00114 const string& LDAPUrl::getFilter() const 
00115 {
00116     return m_Filter;
00117 }
00118 void LDAPUrl::setFilter( const std::string &filter )
00119 {
00120     m_Filter = filter;
00121     regenerate = true;
00122 }
00123 
00124 const StringList& LDAPUrl::getAttrs() const 
00125 {
00126     return m_Attrs;
00127 }
00128 void LDAPUrl::setAttrs( const StringList &attrs )
00129 {
00130     m_Attrs = attrs;
00131     regenerate = true;
00132 }
00133 
00134 const StringList& LDAPUrl::getExtensions() const 
00135 {
00136     return m_Extensions;
00137 }
00138 
00139 void LDAPUrl::setExtensions( const StringList &ext )
00140 {
00141     m_Extensions = ext;
00142     regenerate = true;
00143 }
00144 
00145 const std::string& LDAPUrl::getScheme() const
00146 {
00147     return m_Scheme;
00148 }
00149 
00150 void LDAPUrl::setScheme( const std::string &scheme )
00151 {
00152     if (scheme == "ldap" || scheme == "ldaps" || 
00153             scheme == "ldapi" || scheme == "cldap" ) 
00154     {
00155         m_Scheme = scheme;
00156         regenerate = true;
00157     } else {
00158         throw LDAPUrlException(LDAPUrlException::INVALID_SCHEME,
00159                 "Unknown URL scheme: \"" + scheme + "\"");
00160     }
00161 }
00162 
00163 void LDAPUrl::parseUrl() 
00164 {
00165     DEBUG(LDAP_DEBUG_TRACE, "LDAPUrl::parseUrl()" << std::endl);
00166     // reading Scheme
00167     std::string::size_type pos = m_urlString.find(':');
00168     std::string::size_type startpos = pos;
00169     if (pos == std::string::npos) {
00170         throw LDAPUrlException(LDAPUrlException::INVALID_URL,
00171                 "No colon found in URL");
00172     }
00173     std::string scheme = m_urlString.substr(0, pos);
00174     DEBUG(LDAP_DEBUG_TRACE, "    scheme is <" << scheme << ">" << std::endl);
00175 
00176     if ( scheme == "ldap" ) {
00177         m_Scheme = scheme;
00178     } else if ( scheme == "ldaps" ) {
00179         m_Scheme = scheme;
00180     } else if ( scheme == "ldapi" ) {
00181         m_Scheme = scheme;
00182     } else if ( scheme == "cldap" ) {
00183         m_Scheme = scheme;
00184     } else {
00185         throw LDAPUrlException(LDAPUrlException::INVALID_SCHEME,
00186                 "Unknown URL Scheme: \"" + scheme + "\"");
00187     }
00188 
00189     if ( m_urlString[pos+1] != '/' || m_urlString[pos+2] != '/' ) {
00190         throw LDAPUrlException(LDAPUrlException::INVALID_URL);
00191     } else {
00192         startpos = pos + 3;
00193     }
00194     if ( m_urlString[startpos] == '/' ) {
00195         // no hostname and port
00196         startpos++;
00197     } else {
00198         std::string::size_type hostend, portstart=0;
00199         pos = m_urlString.find('/', startpos);
00200 
00201         // IPv6 Address?
00202         if ( m_urlString[startpos] == '[' ) {
00203             // skip
00204             startpos++;
00205             hostend =  m_urlString.find(']', startpos);
00206             if ( hostend == std::string::npos ){
00207                 throw LDAPUrlException(LDAPUrlException::INVALID_URL);
00208             }
00209             portstart = hostend + 1;
00210         } else {
00211             hostend = m_urlString.find(':', startpos);
00212             if ( hostend == std::string::npos || portstart > pos ) {
00213                 hostend = pos;
00214             }
00215             portstart = hostend;
00216         }
00217         std::string host = m_urlString.substr(startpos, hostend - startpos);
00218         DEBUG(LDAP_DEBUG_TRACE, "    host: <" << host << ">" << std::endl);
00219         percentDecode(host, m_Host);
00220 
00221         if (portstart >= m_urlString.length() || portstart >= pos ) {
00222             if ( m_Scheme == "ldap" || m_Scheme == "cldap" ) {
00223                 m_Port = LDAP_DEFAULT_PORT;
00224             } else if ( m_Scheme == "ldaps" ) {
00225                 m_Port = LDAPS_DEFAULT_PORT;
00226             }
00227         } else {
00228             std::string port = m_urlString.substr(portstart+1, 
00229                     (pos == std::string::npos ? pos : pos-portstart-1) );
00230             if ( port.length() > 0 ) {
00231                 std::istringstream i(port);
00232                 i >> m_Port;
00233                 if ( i.fail() ){
00234                     throw LDAPUrlException(LDAPUrlException::INVALID_PORT);
00235                 }
00236             }
00237             DEBUG(LDAP_DEBUG_TRACE, "    Port: <" << m_Port << ">" 
00238                     << std::endl);
00239         }
00240         startpos = pos + 1;
00241     }
00242     int parserMode = base;
00243     while ( pos != std::string::npos ) {
00244         pos = m_urlString.find('?', startpos);
00245         std::string actComponent = m_urlString.substr(startpos, 
00246                 pos - startpos);
00247         DEBUG(LDAP_DEBUG_TRACE, "    ParserMode:" << parserMode << std::endl);
00248         DEBUG(LDAP_DEBUG_TRACE, "    ActComponent: <" << actComponent << ">" 
00249                 << std::endl);
00250         std::string s_scope = "";
00251         std::string s_ext = "";
00252         switch(parserMode) {
00253             case base :
00254                 percentDecode(actComponent, m_DN);
00255                 DEBUG(LDAP_DEBUG_TRACE, "    BaseDN:" << m_DN << std::endl); 
00256                 break;
00257             case attrs :
00258                 DEBUG(LDAP_DEBUG_TRACE, "    reading Attributes" << std::endl);
00259                 if (actComponent.length() != 0 ) {
00260                     string2list(actComponent,m_Attrs, true);
00261                 }
00262                 break;
00263             case scope :
00264                 percentDecode(actComponent, s_scope);
00265                 if (s_scope == "base" || s_scope == "" ) {
00266                     m_Scope = 0;
00267                 } else if (s_scope == "one" ) {
00268                     m_Scope = 1;
00269                 } else if (s_scope == "sub" ) {
00270                     m_Scope = 2;
00271                 } else {
00272                     throw LDAPUrlException(LDAPUrlException::INVALID_SCOPE);
00273                 }
00274                 DEBUG(LDAP_DEBUG_TRACE, "    Scope: <" << s_scope << ">"
00275                         << std::endl);
00276                 break;
00277             case filter :
00278                 percentDecode(actComponent, m_Filter);
00279                 DEBUG(LDAP_DEBUG_TRACE, "    filter: <" << m_Filter << ">"
00280                         << std::endl);
00281                 break;
00282             case extensions :
00283                 DEBUG(LDAP_DEBUG_TRACE, "    reading Extensions" << std::endl); 
00284                 string2list(actComponent, m_Extensions, true);
00285                 break;
00286             default : 
00287                 DEBUG(LDAP_DEBUG_TRACE, "    unknown state" << std::endl); 
00288                 break;
00289         }
00290         startpos = pos + 1;
00291         parserMode++;
00292     }
00293 }
00294 
00295 void LDAPUrl::percentDecode(const std::string& src, std::string &out)
00296 {
00297     DEBUG(LDAP_DEBUG_TRACE, "LDAPUrl::percentDecode()" << std::endl); 
00298     std::string::size_type pos = 0;
00299     std::string::size_type startpos = 0;
00300     pos = src.find('%', startpos);
00301     while ( pos != std::string::npos ) {
00302         out += src.substr(startpos, pos - startpos);
00303         std::string istr(src.substr(pos+1, 2));
00304         std::istringstream i(istr);
00305         i.setf(std::ios::hex, std::ios::basefield);
00306         i.unsetf(std::ios::showbase);
00307         int hex;
00308         i >> hex;
00309         if ( i.fail() ){
00310             throw LDAPUrlException(LDAPUrlException::URL_DECODING_ERROR, 
00311                     "Invalid percent encoding");
00312         }
00313         char j = hex;
00314         out.push_back(j);
00315         startpos = pos+3;
00316         pos = src.find('%', startpos);
00317     } 
00318     out += src.substr(startpos, pos - startpos);
00319 }
00320 
00321 void LDAPUrl::string2list(const std::string &src, StringList& sl, 
00322             bool percentDecode)
00323 {
00324     std::string::size_type comma_startpos = 0;
00325     std::string::size_type comma_pos = 0;
00326     std::string actItem;
00327     while ( comma_pos != std::string::npos ) {
00328         comma_pos = src.find(',', comma_startpos);
00329         actItem = src.substr(comma_startpos, comma_pos - comma_startpos);
00330         if (percentDecode){
00331             std::string decoded;
00332             this->percentDecode(actItem,decoded);
00333             actItem = decoded;
00334         }
00335         sl.add(actItem);
00336         comma_startpos = comma_pos + 1;
00337     }
00338 }
00339 
00340 
00341 void LDAPUrl::components2Url() const
00342 {
00343     std::ostringstream url; 
00344     std::string encoded = "";
00345     
00346     url << m_Scheme << "://";
00347     // IPv6 ?
00348     if ( m_Host.find( ':', 0 ) != std::string::npos ) {
00349         url <<  "[" << this->percentEncode(m_Host, encoded) <<  "]";
00350     } else {
00351         url << this->percentEncode(m_Host, encoded, PCT_ENCFLAG_SLASH);
00352     }
00353 
00354     if ( m_Port != 0 ) {
00355         url << ":" << m_Port;
00356     }
00357 
00358     url << "/";
00359     encoded = "";
00360     if ( m_DN != "" ) {
00361         this->percentEncode( m_DN, encoded );
00362         url << encoded;
00363     }
00364     string qm = "";
00365     if ( ! m_Attrs.empty() ){
00366         url << "?";
00367         bool first = true;
00368         for ( StringList::const_iterator i = m_Attrs.begin();
00369                 i != m_Attrs.end(); i++) 
00370         {
00371             this->percentEncode( *i, encoded );
00372             if ( ! first ) {
00373                 url << ",";
00374             } else {
00375                 first = false;
00376             }
00377             url << encoded;
00378         }
00379     } else {
00380         qm.append("?");
00381     }
00382     if ( m_Scope == 1 ) {
00383         url << qm << "?one";
00384         qm = "";
00385     } else if ( m_Scope == 2 ) {
00386         url << qm << "?sub";
00387         qm = "";
00388     } else {
00389         qm.append("?");
00390     }
00391     if (m_Filter != "" ){
00392         this->percentEncode( m_Filter, encoded );
00393         url << qm << "?" << encoded;
00394         qm = "";
00395     } else {
00396         qm.append("?");
00397     }
00398 
00399     if ( ! m_Extensions.empty() ){
00400         url << qm << "?";
00401         bool first = true;
00402         for ( StringList::const_iterator i = m_Extensions.begin();
00403                 i != m_Extensions.end(); i++) 
00404         {
00405             this->percentEncode( *i, encoded, 1);
00406             if ( ! first ) {
00407                 url << ",";
00408             } else {
00409                 first = false;
00410             }
00411             url << encoded;
00412         }
00413     }
00414     m_urlString=url.str();  
00415 }
00416 
00417 
00418 std::string& LDAPUrl::percentEncode( const std::string &src, 
00419         std::string &dest, 
00420         int flags) const
00421 {
00422     std::ostringstream o;
00423     o.setf(std::ios::hex, std::ios::basefield);
00424     o.setf(std::ios::uppercase);
00425     o.unsetf(std::ios::showbase);
00426     bool escape=false;
00427     for ( std::string::const_iterator i = src.begin(); i != src.end(); i++ ){
00428         switch(*i){
00429             /* reserved */
00430             case '?' :
00431                 escape = true;
00432             break;
00433             case ',' :
00434                 if ( flags & PCT_ENCFLAG_COMMA ) {
00435                     escape = true;
00436                 } else {
00437                     escape = false;
00438                 }
00439             break;
00440             case ':' :
00441             case '/' :
00442                 if ( flags & PCT_ENCFLAG_SLASH ) {
00443                     escape = true;
00444                 } else {
00445                     escape = false;
00446                 }
00447             break;
00448             case '#' :
00449             case '[' :
00450             case ']' :
00451             case '@' :
00452             case '!' :
00453             case '$' :
00454             case '&' :
00455             case '\'' :
00456             case '(' :
00457             case ')' :
00458             case '*' :
00459             case '+' :
00460             case ';' :
00461             case '=' :
00462             /* unreserved */
00463             case '-' :
00464             case '.' :
00465             case '_' :
00466             case '~' :
00467                 escape = false;
00468             break;
00469             default :
00470                 if (  std::isalnum(*i) ) {
00471                     escape = false;
00472                 } else {
00473                     escape = true;
00474                 }
00475             break;
00476         }
00477         if ( escape ) {
00478             o << "%" << std::setw(2) << std::setfill('0') << (int)(unsigned char)*i ;
00479         } else {
00480             o.put(*i);
00481         }
00482     }
00483     dest = o.str();
00484     return dest;
00485 }
00486 
00487 const code2string_s LDAPUrlException::code2string[] = {
00488    { INVALID_SCHEME,            "Invalid URL Scheme" },
00489    { INVALID_PORT,              "Invalid Port in Url" },
00490    { INVALID_SCOPE,             "Invalid Search Scope in Url" },
00491    { INVALID_URL,               "Invalid LDAP Url" },
00492    { URL_DECODING_ERROR,        "Url-decoding Error" },
00493    { 0, 0 }
00494 };
00495 
00496 LDAPUrlException::LDAPUrlException( int code, const std::string &msg) :
00497     m_code(code), m_addMsg(msg) {}
00498 
00499 int LDAPUrlException::getCode() const
00500 {
00501     return m_code;
00502 }
00503 
00504 const std::string LDAPUrlException::getAdditionalInfo() const
00505 {
00506     return m_addMsg;
00507 }
00508 
00509 const std::string LDAPUrlException::getErrorMessage() const
00510 {
00511     for ( int i = 0; code2string[i].string != 0; i++ ) {
00512         if ( code2string[i].code == m_code ) {
00513             return std::string(code2string[i].string);
00514         }
00515     }
00516     return "";
00517 
00518 }