1 // $OpenLDAP: pkg/ldap/contrib/ldapc++/src/LDAPUrl.cpp,v 1.3.10.5 2008/04/14 23:09:26 quanah Exp $ 2 /* 3 * Copyright 2000-2006, OpenLDAP Foundation, All Rights Reserved. 4 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file 5 */ 6 7 8 #include "LDAPUrl.h" 9 #include <sstream> 10 #include <iomanip> 11 #include "debug.h" 12 13 using namespace std; 14 15 #define PCT_ENCFLAG_NONE 0x0000U 16 #define PCT_ENCFLAG_COMMA 0x0001U 17 #define PCT_ENCFLAG_SLASH 0x0002U 18 19 #define LDAP_DEFAULT_PORT 389 20 #define LDAPS_DEFAULT_PORT 636 21 22 LDAPUrl::LDAPUrl(const std::string &url) 23 { 24 DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPUrl::LDAPUrl()" << endl); 25 DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER, 26 " url:" << url << endl); 27 m_urlString = url; 28 m_Filter = ""; 29 m_Scheme = "ldap"; 30 m_Scope = 0; 31 m_Port = 0; 32 regenerate = false; 33 if (url != "") { 34 this->parseUrl(); 35 } 36 } 37 38 LDAPUrl::~LDAPUrl() 39 { 40 DEBUG(LDAP_DEBUG_DESTROY, "LDAPUrl::~LDAPUrl()" << endl); 41 m_Attrs.clear(); 42 } 43 44 int LDAPUrl::getPort() const 45 { 46 return m_Port; 47 } 48 49 void LDAPUrl::setPort(int port) 50 { 51 m_Port = port; 52 regenerate = true; 53 } 54 55 int LDAPUrl::getScope() const 56 { 57 return m_Scope; 58 } 59 60 void LDAPUrl::setScope( const std::string &scope ) 61 { 62 if (scope == "base" || scope == "" ) { 63 m_Scope = 0; 64 } else if (scope == "one" ) { 65 m_Scope = 1; 66 } else if (scope == "sub" ) { 67 m_Scope = 2; 68 } else { 69 throw LDAPUrlException(LDAPUrlException::INVALID_SCOPE, 70 "Scope was:" + scope); 71 } 72 regenerate = true; 73 } 74 75 const string& LDAPUrl::getURLString() const 76 { 77 if (regenerate){ 78 this->components2Url(); 79 regenerate=false; 80 } 81 return m_urlString; 82 } 83 84 void LDAPUrl::setURLString( const std::string &url ) 85 { 86 m_urlString = url; 87 if (url != "") { 88 this->parseUrl(); 89 } 90 regenerate = false; 91 } 92 93 const string& LDAPUrl::getHost() const 94 { 95 return m_Host; 96 } 97 98 void LDAPUrl::setHost( const std::string &host ) 99 { 100 m_Host = host; 101 regenerate = true; 102 } 103 104 const string& LDAPUrl::getDN() const 105 { 106 return m_DN; 107 } 108 void LDAPUrl::setDN( const std::string &dn ) 109 { 110 m_DN = dn; 111 regenerate = true; 112 } 113 114 const string& LDAPUrl::getFilter() const 115 { 116 return m_Filter; 117 } 118 void LDAPUrl::setFilter( const std::string &filter ) 119 { 120 m_Filter = filter; 121 regenerate = true; 122 } 123 124 const StringList& LDAPUrl::getAttrs() const 125 { 126 return m_Attrs; 127 } 128 void LDAPUrl::setAttrs( const StringList &attrs ) 129 { 130 m_Attrs = attrs; 131 regenerate = true; 132 } 133 134 const StringList& LDAPUrl::getExtensions() const 135 { 136 return m_Extensions; 137 } 138 139 void LDAPUrl::setExtensions( const StringList &ext ) 140 { 141 m_Extensions = ext; 142 regenerate = true; 143 } 144 145 const std::string& LDAPUrl::getScheme() const 146 { 147 return m_Scheme; 148 } 149 150 void LDAPUrl::setScheme( const std::string &scheme ) 151 { 152 if (scheme == "ldap" || scheme == "ldaps" || 153 scheme == "ldapi" || scheme == "cldap" ) 154 { 155 m_Scheme = scheme; 156 regenerate = true; 157 } else { 158 throw LDAPUrlException(LDAPUrlException::INVALID_SCHEME, 159 "Unknown URL scheme: \"" + scheme + "\""); 160 } 161 } 162 163 void LDAPUrl::parseUrl() 164 { 165 DEBUG(LDAP_DEBUG_TRACE, "LDAPUrl::parseUrl()" << std::endl); 166 // reading Scheme 167 std::string::size_type pos = m_urlString.find(':'); 168 std::string::size_type startpos = pos; 169 if (pos == std::string::npos) { 170 throw LDAPUrlException(LDAPUrlException::INVALID_URL, 171 "No colon found in URL"); 172 } 173 std::string scheme = m_urlString.substr(0, pos); 174 DEBUG(LDAP_DEBUG_TRACE, " scheme is <" << scheme << ">" << std::endl); 175 176 if ( scheme == "ldap" ) { 177 m_Scheme = scheme; 178 } else if ( scheme == "ldaps" ) { 179 m_Scheme = scheme; 180 } else if ( scheme == "ldapi" ) { 181 m_Scheme = scheme; 182 } else if ( scheme == "cldap" ) { 183 m_Scheme = scheme; 184 } else { 185 throw LDAPUrlException(LDAPUrlException::INVALID_SCHEME, 186 "Unknown URL Scheme: \"" + scheme + "\""); 187 } 188 189 if ( m_urlString[pos+1] != '/' || m_urlString[pos+2] != '/' ) { 190 throw LDAPUrlException(LDAPUrlException::INVALID_URL); 191 } else { 192 startpos = pos + 3; 193 } 194 if ( m_urlString[startpos] == '/' ) { 195 // no hostname and port 196 startpos++; 197 } else { 198 std::string::size_type hostend; 199 std::string::size_type portstart; 200 pos = m_urlString.find('/', startpos); 201 202 // IPv6 Address? 203 if ( m_urlString[startpos] == '[' ) { 204 // skip 205 startpos++; 206 hostend = m_urlString.find(']', startpos); 207 if ( hostend == std::string::npos ){ 208 throw LDAPUrlException(LDAPUrlException::INVALID_URL); 209 } 210 portstart = hostend + 1; 211 } else { 212 hostend = m_urlString.find(':', startpos); 213 if ( hostend == std::string::npos || portstart > pos ) { 214 hostend = pos; 215 } 216 portstart = hostend; 217 } 218 std::string host = m_urlString.substr(startpos, hostend - startpos); 219 DEBUG(LDAP_DEBUG_TRACE, " host: <" << host << ">" << std::endl); 220 percentDecode(host, m_Host); 221 222 if (portstart >= m_urlString.length() || portstart >= pos ) { 223 if ( m_Scheme == "ldap" || m_Scheme == "cldap" ) { 224 m_Port = LDAP_DEFAULT_PORT; 225 } else if ( m_Scheme == "ldaps" ) { 226 m_Port = LDAPS_DEFAULT_PORT; 227 } 228 } else { 229 std::string port = m_urlString.substr(portstart+1, 230 (pos == std::string::npos ? pos : pos-portstart-1) ); 231 if ( port.length() > 0 ) { 232 std::istringstream i(port); 233 i >> m_Port; 234 if ( i.fail() ){ 235 throw LDAPUrlException(LDAPUrlException::INVALID_PORT); 236 } 237 } 238 DEBUG(LDAP_DEBUG_TRACE, " Port: <" << m_Port << ">" 239 << std::endl); 240 } 241 startpos = pos + 1; 242 } 243 int parserMode = base; 244 while ( pos != std::string::npos ) { 245 pos = m_urlString.find('?', startpos); 246 std::string actComponent = m_urlString.substr(startpos, 247 pos - startpos); 248 DEBUG(LDAP_DEBUG_TRACE, " ParserMode:" << parserMode << std::endl); 249 DEBUG(LDAP_DEBUG_TRACE, " ActComponent: <" << actComponent << ">" 250 << std::endl); 251 std::string s_scope = ""; 252 std::string s_ext = ""; 253 switch(parserMode) { 254 case base : 255 percentDecode(actComponent, m_DN); 256 DEBUG(LDAP_DEBUG_TRACE, " BaseDN:" << m_DN << std::endl); 257 break; 258 case attrs : 259 DEBUG(LDAP_DEBUG_TRACE, " reading Attributes" << std::endl); 260 if (actComponent.length() != 0 ) { 261 string2list(actComponent,m_Attrs, true); 262 } 263 break; 264 case scope : 265 percentDecode(actComponent, s_scope); 266 if (s_scope == "base" || s_scope == "" ) { 267 m_Scope = 0; 268 } else if (s_scope == "one" ) { 269 m_Scope = 1; 270 } else if (s_scope == "sub" ) { 271 m_Scope = 2; 272 } else { 273 throw LDAPUrlException(LDAPUrlException::INVALID_SCOPE); 274 } 275 DEBUG(LDAP_DEBUG_TRACE, " Scope: <" << s_scope << ">" 276 << std::endl); 277 break; 278 case filter : 279 percentDecode(actComponent, m_Filter); 280 DEBUG(LDAP_DEBUG_TRACE, " filter: <" << m_Filter << ">" 281 << std::endl); 282 break; 283 case extensions : 284 DEBUG(LDAP_DEBUG_TRACE, " reading Extensions" << std::endl); 285 string2list(actComponent, m_Extensions, true); 286 break; 287 default : 288 DEBUG(LDAP_DEBUG_TRACE, " unknown state" << std::endl); 289 break; 290 } 291 startpos = pos + 1; 292 parserMode++; 293 } 294 } 295 296 void LDAPUrl::percentDecode(const std::string& src, std::string &out) 297 { 298 DEBUG(LDAP_DEBUG_TRACE, "LDAPUrl::percentDecode()" << std::endl); 299 std::string::size_type pos = 0; 300 std::string::size_type startpos = 0; 301 pos = src.find('%', startpos); 302 while ( pos != std::string::npos ) { 303 out += src.substr(startpos, pos - startpos); 304 std::string istr(src.substr(pos+1, 2)); 305 std::istringstream i(istr); 306 i.setf(std::ios::hex, std::ios::basefield); 307 i.unsetf(std::ios::showbase); 308 int hex; 309 i >> hex; 310 if ( i.fail() ){ 311 throw LDAPUrlException(LDAPUrlException::URL_DECODING_ERROR, 312 "Invalid percent encoding"); 313 } 314 char j = hex; 315 out.push_back(j); 316 startpos = pos+3; 317 pos = src.find('%', startpos); 318 } 319 out += src.substr(startpos, pos - startpos); 320 } 321 322 void LDAPUrl::string2list(const std::string &src, StringList& sl, 323 bool percentDecode) 324 { 325 std::string::size_type comma_startpos = 0; 326 std::string::size_type comma_pos = 0; 327 std::string actItem; 328 while ( comma_pos != std::string::npos ) { 329 comma_pos = src.find(',', comma_startpos); 330 actItem = src.substr(comma_startpos, comma_pos - comma_startpos); 331 if (percentDecode){ 332 std::string decoded; 333 this->percentDecode(actItem,decoded); 334 actItem = decoded; 335 } 336 sl.add(actItem); 337 comma_startpos = comma_pos + 1; 338 } 339 } 340 341 342 void LDAPUrl::components2Url() const 343 { 344 std::ostringstream url; 345 std::string encoded = ""; 346 347 url << m_Scheme << "://"; 348 // IPv6 ? 349 if ( m_Host.find( ':', 0 ) != std::string::npos ) { 350 url << "[" << this->percentEncode(m_Host, encoded) << "]"; 351 } else { 352 url << this->percentEncode(m_Host, encoded, PCT_ENCFLAG_SLASH); 353 } 354 355 if ( m_Port != 0 ) { 356 url << ":" << m_Port; 357 } 358 359 url << "/"; 360 encoded = ""; 361 if ( m_DN != "" ) { 362 this->percentEncode( m_DN, encoded ); 363 url << encoded; 364 } 365 string qm = ""; 366 if ( ! m_Attrs.empty() ){ 367 url << "?"; 368 bool first = true; 369 for ( StringList::const_iterator i = m_Attrs.begin(); 370 i != m_Attrs.end(); i++) 371 { 372 this->percentEncode( *i, encoded ); 373 if ( ! first ) { 374 url << ","; 375 } else { 376 first = false; 377 } 378 url << encoded; 379 } 380 } else { 381 qm.append("?"); 382 } 383 if ( m_Scope == 1 ) { 384 url << qm << "?one"; 385 qm = ""; 386 } else if ( m_Scope == 2 ) { 387 url << qm << "?sub"; 388 qm = ""; 389 } else { 390 qm.append("?"); 391 } 392 if (m_Filter != "" ){ 393 this->percentEncode( m_Filter, encoded ); 394 url << qm << "?" << encoded; 395 qm = ""; 396 } else { 397 qm.append("?"); 398 } 399 400 if ( ! m_Extensions.empty() ){ 401 url << qm << "?"; 402 bool first = true; 403 for ( StringList::const_iterator i = m_Extensions.begin(); 404 i != m_Extensions.end(); i++) 405 { 406 this->percentEncode( *i, encoded, 1); 407 if ( ! first ) { 408 url << ","; 409 } else { 410 first = false; 411 } 412 url << encoded; 413 } 414 } 415 m_urlString=url.str(); 416 } 417 418 419 std::string& LDAPUrl::percentEncode( const std::string &src, 420 std::string &dest, 421 int flags) const 422 { 423 std::ostringstream o; 424 o.setf(std::ios::hex, std::ios::basefield); 425 o.setf(std::ios::uppercase); 426 o.unsetf(std::ios::showbase); 427 bool escape=false; 428 for ( std::string::const_iterator i = src.begin(); i != src.end(); i++ ){ 429 switch(*i){ 430 /* reserved */ 431 case '?' : 432 escape = true; 433 break; 434 case ',' : 435 if ( flags & PCT_ENCFLAG_COMMA ) { 436 escape = true; 437 } else { 438 escape = false; 439 } 440 break; 441 case ':' : 442 case '/' : 443 if ( flags & PCT_ENCFLAG_SLASH ) { 444 escape = true; 445 } else { 446 escape = false; 447 } 448 break; 449 case '#' : 450 case '[' : 451 case ']' : 452 case '@' : 453 case '!' : 454 case '$' : 455 case '&' : 456 case '\'' : 457 case '(' : 458 case ')' : 459 case '*' : 460 case '+' : 461 case ';' : 462 case '=' : 463 /* unreserved */ 464 case '-' : 465 case '.' : 466 case '_' : 467 case '~' : 468 escape = false; 469 break; 470 default : 471 if ( std::isalnum(*i) ) { 472 escape = false; 473 } else { 474 escape = true; 475 } 476 break; 477 } 478 if ( escape ) { 479 o << "%" << std::setw(2) << std::setfill('0') << (int)(unsigned char)*i ; 480 } else { 481 o.put(*i); 482 } 483 } 484 dest = o.str(); 485 return dest; 486 } 487 488 const code2string_s LDAPUrlException::code2string[] = { 489 { INVALID_SCHEME, "Invalid URL Scheme" }, 490 { INVALID_PORT, "Invalid Port in Url" }, 491 { INVALID_SCOPE, "Invalid Search Scope in Url" }, 492 { INVALID_URL, "Invalid LDAP Url" }, 493 { URL_DECODING_ERROR, "Url-decoding Error" }, 494 { 0, 0 } 495 }; 496 497 LDAPUrlException::LDAPUrlException( int code, const std::string &msg) : 498 m_code(code), m_addMsg(msg) {} 499 500 int LDAPUrlException::getCode() const 501 { 502 return m_code; 503 } 504 505 const std::string LDAPUrlException::getAdditionalInfo() const 506 { 507 return m_addMsg; 508 } 509 510 const std::string LDAPUrlException::getErrorMessage() const 511 { 512 for ( int i = 0; code2string[i].string != 0; i++ ) { 513 if ( code2string[i].code == m_code ) { 514 return std::string(code2string[i].string); 515 } 516 } 517 return ""; 518 519 } 520