]> git.sur5r.net Git - openldap/blob - contrib/ldapc++/src/LDAPUrl.cpp
Fixed parsing of URIs with missing host and/or port
[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 = pos;
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         // no hostname and port
194         startpos++;
195     } else {
196         pos = m_urlString.find('/', startpos);
197         std::string hostport = m_urlString.substr(startpos, 
198                 pos - startpos);
199         DEBUG(LDAP_DEBUG_TRACE, "    hostport: <" << hostport << ">" 
200                 << std::endl);
201         std::string::size_type portstart = m_urlString.find(':', startpos);
202         if (portstart == std::string::npos || portstart > pos ) {
203             percentDecode(hostport, m_Host);
204             if ( m_Scheme == "ldap" || m_Scheme == "cldap" ) {
205                 m_Port = LDAP_DEFAULT_PORT;
206             } else if ( m_Scheme == "ldaps" ) {
207                 m_Port = LDAPS_DEFAULT_PORT;
208             }
209         } else {
210             std::string tmp = m_urlString.substr(startpos, 
211                         portstart - startpos);
212             percentDecode(tmp, m_Host);
213             DEBUG(LDAP_DEBUG_TRACE, "Host: <" << m_Host << ">" << std::endl);
214             std::string port = m_urlString.substr(portstart+1, 
215                     pos-portstart-1);
216             if ( port.length() > 0 ) {
217                 std::istringstream i(port);
218                 i >> m_Port;
219                 if ( i.fail() ){
220                     throw LDAPUrlException(LDAPUrlException::INVALID_PORT);
221                 }
222             }
223             DEBUG(LDAP_DEBUG_TRACE, "    Port: <" << m_Port << ">" 
224                     << std::endl);
225         }
226         startpos = pos + 1;
227     }
228     int parserMode = base;
229     while ( pos != std::string::npos ) {
230         pos = m_urlString.find('?', startpos);
231         std::string actComponent = m_urlString.substr(startpos, 
232                 pos - startpos);
233         DEBUG(LDAP_DEBUG_TRACE, "    ParserMode:" << parserMode << std::endl);
234         DEBUG(LDAP_DEBUG_TRACE, "    ActComponent: <" << actComponent << ">" 
235                 << std::endl);
236         std::string s_scope = "";
237         std::string s_ext = "";
238         switch(parserMode) {
239             case base :
240                 percentDecode(actComponent, m_DN);
241                 DEBUG(LDAP_DEBUG_TRACE, "    BaseDN:" << m_DN << std::endl); 
242                 break;
243             case attrs :
244                 DEBUG(LDAP_DEBUG_TRACE, "    reading Attributes" << std::endl);
245                 if (actComponent.length() != 0 ) {
246                     string2list(actComponent,m_Attrs, true);
247                 }
248                 break;
249             case scope :
250                 percentDecode(actComponent, s_scope);
251                 if (s_scope == "base" || s_scope == "" ) {
252                     m_Scope = 0;
253                 } else if (s_scope == "one" ) {
254                     m_Scope = 1;
255                 } else if (s_scope == "sub" ) {
256                     m_Scope = 2;
257                 } else {
258                     throw LDAPUrlException(LDAPUrlException::INVALID_SCOPE);
259                 }
260                 DEBUG(LDAP_DEBUG_TRACE, "    Scope: <" << s_scope << ">"
261                         << std::endl);
262                 break;
263             case filter :
264                 percentDecode(actComponent, m_Filter);
265                 DEBUG(LDAP_DEBUG_TRACE, "    filter: <" << m_Filter << ">"
266                         << std::endl);
267                 break;
268             case extensions :
269                 DEBUG(LDAP_DEBUG_TRACE, "    reading Extensions" << std::endl); 
270                 string2list(actComponent, m_Extensions, true);
271                 break;
272             default : 
273                 DEBUG(LDAP_DEBUG_TRACE, "    unknown state" << std::endl); 
274                 break;
275         }
276         startpos = pos + 1;
277         parserMode++;
278     }
279 }
280
281 void LDAPUrl::percentDecode(const std::string& src, std::string &out)
282 {
283     DEBUG(LDAP_DEBUG_TRACE, "LDAPUrl::percentDecode()" << std::endl); 
284     std::string::size_type pos = 0;
285     std::string::size_type startpos = 0;
286     pos = src.find('%', startpos);
287     while ( pos != std::string::npos ) {
288         out += src.substr(startpos, pos - startpos);
289         std::string istr(src.substr(pos+1, 2));
290         std::istringstream i(istr);
291         i.setf(std::ios::hex, std::ios::basefield);
292         i.unsetf(std::ios::showbase);
293         int hex;
294         i >> hex;
295         if ( i.fail() ){
296             throw LDAPUrlException(LDAPUrlException::URL_DECODING_ERROR, 
297                     "Invalid percent encoding");
298         }
299         char j = hex;
300         out.push_back(j);
301         startpos = pos+3;
302         pos = src.find('%', startpos);
303     } 
304     out += src.substr(startpos, pos - startpos);
305 }
306
307 void LDAPUrl::string2list(const std::string &src, StringList& sl, 
308             bool percentDecode)
309 {
310     std::string::size_type comma_startpos = 0;
311     std::string::size_type comma_pos = 0;
312     std::string actItem;
313     while ( comma_pos != std::string::npos ) {
314         comma_pos = src.find(',', comma_startpos);
315         actItem = src.substr(comma_startpos, comma_pos - comma_startpos);
316         if (percentDecode){
317             std::string decoded;
318             this->percentDecode(actItem,decoded);
319             actItem = decoded;
320         }
321         sl.add(actItem);
322         comma_startpos = comma_pos + 1;
323     }
324 }
325
326
327 void LDAPUrl::components2Url() const
328 {
329     std::ostringstream url; 
330     std::string encoded = "";
331     this->percentEncode(m_Host, encoded, PCT_ENCFLAG_SLASH);
332     url << m_Scheme << "://" << encoded;
333     if ( m_Port != 0 ) {
334         url << ":" << m_Port;
335     }
336
337     url << "/";
338     encoded = "";
339     if ( m_DN != "" ) {
340         this->percentEncode( m_DN, encoded );
341         url << encoded;
342     }
343     string qm = "";
344     if ( ! m_Attrs.empty() ){
345         url << "?";
346         bool first = true;
347         for ( StringList::const_iterator i = m_Attrs.begin();
348                 i != m_Attrs.end(); i++) 
349         {
350             this->percentEncode( *i, encoded );
351             if ( ! first ) {
352                 url << ",";
353             } else {
354                 first = false;
355             }
356             url << encoded;
357         }
358     } else {
359         qm.append("?");
360     }
361     if ( m_Scope == 1 ) {
362         url << qm << "?one";
363         qm = "";
364     } else if ( m_Scope == 2 ) {
365         url << qm << "?sub";
366         qm = "";
367     } else {
368         qm.append("?");
369     }
370     if (m_Filter != "" ){
371         this->percentEncode( m_Filter, encoded );
372         url << qm << "?" << encoded;
373         qm = "";
374     } else {
375         qm.append("?");
376     }
377
378     if ( ! m_Extensions.empty() ){
379         url << qm << "?";
380         bool first = true;
381         for ( StringList::const_iterator i = m_Extensions.begin();
382                 i != m_Extensions.end(); i++) 
383         {
384             this->percentEncode( *i, encoded, 1);
385             if ( ! first ) {
386                 url << ",";
387             } else {
388                 first = false;
389             }
390             url << encoded;
391         }
392     }
393     m_urlString=url.str();  
394 }
395
396
397 void LDAPUrl::percentEncode( const std::string &src, 
398         std::string &dest, 
399         int flags) const
400 {
401     std::ostringstream o;
402     o.setf(std::ios::hex, std::ios::basefield);
403     o.setf(std::ios::uppercase);
404     o.unsetf(std::ios::showbase);
405     bool escape=false;
406     for ( std::string::const_iterator i = src.begin(); i != src.end(); i++ ){
407         switch(*i){
408             /* reserved */
409             case '?' :
410                 escape = true;
411             break;
412             case ',' :
413                 if ( flags & PCT_ENCFLAG_COMMA ) {
414                     escape = true;
415                 } else {
416                     escape = false;
417                 }
418             break;
419             case ':' :
420             case '/' :
421                 if ( flags & PCT_ENCFLAG_SLASH ) {
422                     escape = true;
423                 } else {
424                     escape = false;
425                 }
426             break;
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             case '=' :
441             /* unreserved */
442             case '-' :
443             case '.' :
444             case '_' :
445             case '~' :
446                 escape = false;
447             break;
448             default :
449                 if (  std::isalnum(*i) ) {
450                     escape = false;
451                 } else {
452                     escape = true;
453                 }
454             break;
455         }
456         if ( escape ) {
457             o << "%" << (int)(unsigned char)*i ;
458         } else {
459             o.put(*i);
460         }
461     }
462     dest = o.str();
463 }
464
465 const code2string_s LDAPUrlException::code2string[] = {
466    { INVALID_SCHEME,            "Invalid URL Scheme" },
467    { INVALID_PORT,              "Invalid Port in Url" },
468    { INVALID_SCOPE,             "Invalid Search Scope in Url" },
469    { INVALID_URL,               "Invalid LDAP Url" },
470    { URL_DECODING_ERROR,        "Url-decoding Error" },
471    { 0, 0 }
472 };
473
474 LDAPUrlException::LDAPUrlException( int code, const std::string &msg) :
475     m_code(code), m_addMsg(msg) {}
476
477 int LDAPUrlException::getCode() const
478 {
479     return m_code;
480 }
481
482 const std::string LDAPUrlException::getAdditionalInfo() const
483 {
484     return m_addMsg;
485 }
486
487 const std::string LDAPUrlException::getErrorMessage() const
488 {
489     for ( int i = 0; code2string[i].string != 0; i++ ) {
490         if ( code2string[i].code == m_code ) {
491             return std::string(code2string[i].string);
492         }
493     }
494     return "";
495
496 }