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