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