]> git.sur5r.net Git - openldap/blob - contrib/ldapc++/src/LDAPUrl.cpp
c6acaba4e04ecb6fbec4d9bb644af0301f797be9
[openldap] / contrib / ldapc++ / src / LDAPUrl.cpp
1 // $OpenLDAP$
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 }