source: XMLIO_V2/external/src/POCO/Foundation/URI.cpp @ 80

Last change on this file since 80 was 80, checked in by ymipsl, 14 years ago

ajout lib externe

  • Property svn:eol-style set to native
File size: 17.0 KB
Line 
1//
2// URI.cpp
3//
4// $Id: //poco/1.3/Foundation/src/URI.cpp#3 $
5//
6// Library: Foundation
7// Package: URI
8// Module:  URI
9//
10// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
11// and Contributors.
12//
13// Permission is hereby granted, free of charge, to any person or organization
14// obtaining a copy of the software and accompanying documentation covered by
15// this license (the "Software") to use, reproduce, display, distribute,
16// execute, and transmit the Software, and to prepare derivative works of the
17// Software, and to permit third-parties to whom the Software is furnished to
18// do so, all subject to the following:
19//
20// The copyright notices in the Software and this entire statement, including
21// the above license grant, this restriction and the following disclaimer,
22// must be included in all copies of the Software, in whole or in part, and
23// all derivative works of the Software, unless such copies or derivative
24// works are solely in the form of machine-executable object code generated by
25// a source language processor.
26//
27// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
30// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
31// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
32// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
33// DEALINGS IN THE SOFTWARE.
34//
35
36
37#include <Poco/URI.h>
38#include <Poco/NumberFormatter.h>
39#include <Poco/Exception.h>
40#include <Poco/String.h>
41#include <Poco/NumberParser.h>
42
43
44namespace Poco {
45
46
47const std::string URI::RESERVED_PATH     = "?#";
48const std::string URI::RESERVED_QUERY    = "#";
49const std::string URI::RESERVED_FRAGMENT = "";
50const std::string URI::ILLEGAL           = "%<>{}|\\\"^`";
51
52
53URI::URI():
54        _port(0)
55{
56}
57
58
59URI::URI(const std::string& uri):
60        _port(0)
61{
62        parse(uri);
63}
64
65
66URI::URI(const char* uri):
67        _port(0)
68{
69        parse(std::string(uri));
70}
71
72       
73URI::URI(const std::string& scheme, const std::string& pathEtc):
74        _scheme(scheme),
75        _port(0)
76{
77        toLowerInPlace(_scheme);
78        _port = getWellKnownPort();
79        std::string::const_iterator beg = pathEtc.begin();
80        std::string::const_iterator end = pathEtc.end();
81        parsePathEtc(beg, end);
82}
83
84       
85URI::URI(const std::string& scheme, const std::string& authority, const std::string& pathEtc):
86        _scheme(scheme)
87{
88        toLowerInPlace(_scheme);
89        std::string::const_iterator beg = authority.begin();
90        std::string::const_iterator end = authority.end();
91        parseAuthority(beg, end);
92        beg = pathEtc.begin();
93        end = pathEtc.end();
94        parsePathEtc(beg, end);
95}
96
97
98URI::URI(const std::string& scheme, const std::string& authority, const std::string& path, const std::string& query):
99        _scheme(scheme),
100        _path(path),
101        _query(query)
102{
103        toLowerInPlace(_scheme);
104        std::string::const_iterator beg = authority.begin();
105        std::string::const_iterator end = authority.end();
106        parseAuthority(beg, end);
107}
108
109
110URI::URI(const std::string& scheme, const std::string& authority, const std::string& path, const std::string& query, const std::string& fragment):
111        _scheme(scheme),
112        _path(path),
113        _query(query),
114        _fragment(fragment)
115{
116        toLowerInPlace(_scheme);
117        std::string::const_iterator beg = authority.begin();
118        std::string::const_iterator end = authority.end();
119        parseAuthority(beg, end);
120}
121
122
123URI::URI(const URI& uri):
124        _scheme(uri._scheme),
125        _userInfo(uri._userInfo),
126        _host(uri._host),
127        _port(uri._port),
128        _path(uri._path),
129        _query(uri._query),
130        _fragment(uri._fragment)
131{
132}
133
134       
135URI::URI(const URI& baseURI, const std::string& relativeURI):
136        _scheme(baseURI._scheme),
137        _userInfo(baseURI._userInfo),
138        _host(baseURI._host),
139        _port(baseURI._port),
140        _path(baseURI._path),
141        _query(baseURI._query),
142        _fragment(baseURI._fragment)
143{
144        resolve(relativeURI);
145}
146
147
148URI::~URI()
149{
150}
151
152
153URI& URI::operator = (const URI& uri)
154{
155        if (&uri != this)
156        {
157                _scheme   = uri._scheme;
158                _userInfo = uri._userInfo;
159                _host     = uri._host;
160                _port     = uri._port;
161                _path     = uri._path;
162                _query    = uri._query;
163                _fragment = uri._fragment;
164        }
165        return *this;
166}
167
168       
169URI& URI::operator = (const std::string& uri)
170{
171        clear();
172        parse(uri);
173        return *this;
174}
175
176
177URI& URI::operator = (const char* uri)
178{
179        clear();
180        parse(std::string(uri));
181        return *this;
182}
183
184
185void URI::swap(URI& uri)
186{
187        std::swap(_scheme, uri._scheme);
188        std::swap(_userInfo, uri._userInfo);
189        std::swap(_host, uri._host);
190        std::swap(_port, uri._port);
191        std::swap(_path, uri._path);
192        std::swap(_query, uri._query);
193        std::swap(_fragment, uri._fragment);
194}
195
196
197void URI::clear()
198{
199        _scheme.clear();
200        _userInfo.clear();
201        _host.clear();
202        _port = 0;
203        _path.clear();
204        _query.clear();
205        _fragment.clear();
206}
207
208
209std::string URI::toString() const
210{
211        std::string uri;
212        if (isRelative())
213        {
214                encode(_path, RESERVED_PATH, uri);
215        }
216        else
217        {
218                uri = _scheme;
219                uri += ':';
220                std::string auth = getAuthority();
221                if (!auth.empty() || _scheme == "file")
222                {
223                        uri.append("//");
224                        uri.append(auth);
225                }
226                if (!_path.empty())
227                {
228                        if (!auth.empty() && _path[0] != '/')
229                                uri += '/';
230                        encode(_path, RESERVED_PATH, uri);
231                }
232        }
233        if (!_query.empty())
234        {
235                uri += '?';
236                uri.append(_query);
237        }
238        if (!_fragment.empty())
239        {
240                uri += '#';
241                encode(_fragment, RESERVED_FRAGMENT, uri);
242        }
243        return uri;
244}
245
246
247void URI::setScheme(const std::string& scheme)
248{
249        _scheme = scheme;
250        toLowerInPlace(_scheme);
251        if (_port == 0)
252                _port = getWellKnownPort();
253}
254
255       
256void URI::setUserInfo(const std::string& userInfo)
257{
258        _userInfo.clear();
259        decode(userInfo, _userInfo);
260}
261
262       
263void URI::setHost(const std::string& host)
264{
265        _host = host;
266}
267
268
269unsigned short URI::getPort() const
270{
271        if (_port == 0)
272                return getWellKnownPort();
273        else
274                return _port;
275}
276
277
278void URI::setPort(unsigned short port)
279{
280        _port = port;
281}
282
283       
284std::string URI::getAuthority() const
285{
286        std::string auth;
287        if (!_userInfo.empty())
288        {
289                auth.append(_userInfo);
290                auth += '@';
291        }
292        auth.append(_host);
293        if (_port && !isWellKnownPort())
294        {
295                auth += ':';
296                NumberFormatter::append(auth, _port);
297        }
298        return auth;
299}
300
301       
302void URI::setAuthority(const std::string& authority)
303{
304        _userInfo.clear();
305        _host.clear();
306        _port = 0;
307        std::string::const_iterator beg = authority.begin();
308        std::string::const_iterator end = authority.end();
309        parseAuthority(beg, end);
310}
311
312       
313void URI::setPath(const std::string& path)
314{
315        _path.clear();
316        decode(path, _path);
317}
318
319       
320void URI::setRawQuery(const std::string& query)
321{
322        _query = query;
323}
324
325
326void URI::setQuery(const std::string& query)
327{
328        _query.clear();
329        encode(query, RESERVED_QUERY, _query);
330}
331
332
333std::string URI::getQuery() const
334{
335        std::string query;
336        decode(_query, query);
337        return query;
338}
339
340
341void URI::setFragment(const std::string& fragment)
342{
343        _fragment.clear();
344        decode(fragment, _fragment);
345}
346
347
348void URI::setPathEtc(const std::string& pathEtc)
349{
350        _path.clear();
351        _query.clear();
352        _fragment.clear();
353        std::string::const_iterator beg = pathEtc.begin();
354        std::string::const_iterator end = pathEtc.end();
355        parsePathEtc(beg, end);
356}
357
358       
359std::string URI::getPathEtc() const
360{
361        std::string pathEtc;
362        encode(_path, RESERVED_PATH, pathEtc);
363        if (!_query.empty())
364        {
365                pathEtc += '?';
366                encode(_query, RESERVED_QUERY, pathEtc);
367        }
368        if (!_fragment.empty())
369        {
370                pathEtc += '#';
371                encode(_fragment, RESERVED_FRAGMENT, pathEtc);
372        }
373        return pathEtc;
374}
375
376
377std::string URI::getPathAndQuery() const
378{
379        std::string pathAndQuery;
380        encode(_path, RESERVED_PATH, pathAndQuery);
381        if (!_query.empty())
382        {
383                pathAndQuery += '?';
384                encode(_query, RESERVED_QUERY, pathAndQuery);
385        }
386        return pathAndQuery;
387}
388
389       
390void URI::resolve(const std::string& relativeURI)
391{
392        URI parsedURI(relativeURI);
393        resolve(parsedURI);
394}
395
396
397void URI::resolve(const URI& relativeURI)
398{
399        if (!relativeURI._scheme.empty())
400        {
401                _scheme   = relativeURI._scheme;
402                _userInfo = relativeURI._userInfo;
403                _host     = relativeURI._host;
404                _port     = relativeURI._port;
405                _path     = relativeURI._path;
406                _query    = relativeURI._query;
407                removeDotSegments();
408        }
409        else
410        {
411                if (!relativeURI._host.empty())
412                {
413                        _userInfo = relativeURI._userInfo;
414                        _host     = relativeURI._host;
415                        _port     = relativeURI._port;
416                        _path     = relativeURI._path;
417                        _query    = relativeURI._query;
418                        removeDotSegments();
419                }
420                else
421                {
422                        if (relativeURI._path.empty())
423                        {
424                                if (!relativeURI._query.empty())
425                                        _query = relativeURI._query;
426                        }
427                        else
428                        {
429                                if (relativeURI._path[0] == '/')
430                                {
431                                        _path = relativeURI._path;
432                                        removeDotSegments();
433                                }
434                                else
435                                {
436                                        mergePath(relativeURI._path);
437                                }
438                                _query = relativeURI._query;
439                        }
440                }
441        }
442        _fragment = relativeURI._fragment;     
443}
444
445
446bool URI::isRelative() const
447{
448        return _scheme.empty();
449}
450
451
452bool URI::empty() const
453{
454        return _scheme.empty() && _host.empty() && _path.empty() && _query.empty() && _fragment.empty();
455}
456
457       
458bool URI::operator == (const URI& uri) const
459{
460        return equals(uri);
461}
462
463
464bool URI::operator == (const std::string& uri) const
465{
466        URI parsedURI(uri);
467        return equals(parsedURI);
468}
469
470
471bool URI::operator != (const URI& uri) const
472{
473        return !equals(uri);
474}
475
476
477bool URI::operator != (const std::string& uri) const
478{
479        URI parsedURI(uri);
480        return !equals(parsedURI);
481}
482
483
484bool URI::equals(const URI& uri) const
485{
486        return _scheme   == uri._scheme
487            && _userInfo == uri._userInfo
488            && _host     == uri._host
489            && getPort() == uri.getPort()
490            && _path     == uri._path
491            && _query    == uri._query
492            && _fragment == uri._fragment;
493}
494
495       
496void URI::normalize()
497{
498        removeDotSegments(!isRelative());
499}
500
501
502void URI::removeDotSegments(bool removeLeading)
503{
504        if (_path.empty()) return;
505       
506        bool leadingSlash  = *(_path.begin()) == '/';
507        bool trailingSlash = *(_path.rbegin()) == '/';
508        std::vector<std::string> segments;
509        std::vector<std::string> normalizedSegments;
510        getPathSegments(segments);
511        for (std::vector<std::string>::const_iterator it = segments.begin(); it != segments.end(); ++it)
512        {
513                if (*it == "..")
514                {
515                        if (!normalizedSegments.empty())
516                        {
517                                if (normalizedSegments.back() == "..")
518                                        normalizedSegments.push_back(*it);
519                                else
520                                        normalizedSegments.pop_back();
521                        }
522                        else if (!removeLeading)
523                        {
524                                normalizedSegments.push_back(*it);
525                        }
526                }
527                else if (*it != ".")
528                {
529                        normalizedSegments.push_back(*it);
530                }
531        }
532        buildPath(normalizedSegments, leadingSlash, trailingSlash);
533}
534
535
536void URI::getPathSegments(std::vector<std::string>& segments)
537{
538        getPathSegments(_path, segments);
539}
540
541
542void URI::getPathSegments(const std::string& path, std::vector<std::string>& segments)
543{
544        std::string::const_iterator it  = path.begin();
545        std::string::const_iterator end = path.end();
546        std::string seg;
547        while (it != end)
548        {
549                if (*it == '/')
550                {
551                        if (!seg.empty())
552                        {
553                                segments.push_back(seg);
554                                seg.clear();
555                        }
556                }
557                else seg += *it;
558                ++it;
559        }
560        if (!seg.empty())
561                segments.push_back(seg);
562}
563
564
565void URI::encode(const std::string& str, const std::string& reserved, std::string& encodedStr)
566{
567        for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
568        {
569                char c = *it;
570                if ((c >= 'a' && c <= 'z') || 
571                    (c >= 'A' && c <= 'Z') || 
572                    (c >= '0' && c <= '9') ||
573                    c == '-' || c == '_' || 
574                    c == '.' || c == '~')
575                {
576                        encodedStr += c;
577                }
578                else if (c <= 0x20 || c >= 0x7F || ILLEGAL.find(c) != std::string::npos || reserved.find(c) != std::string::npos)
579                {
580                        encodedStr += '%';
581                        encodedStr += NumberFormatter::formatHex((unsigned) (unsigned char) c, 2);
582                }
583                else encodedStr += c;
584        }
585}
586
587       
588void URI::decode(const std::string& str, std::string& decodedStr)
589{
590        std::string::const_iterator it  = str.begin();
591        std::string::const_iterator end = str.end();
592        while (it != end)
593        {
594                char c = *it++;
595                if (c == '%')
596                {
597                        if (it == end) throw SyntaxException("URI encoding: no hex digit following percent sign", str);
598                        char hi = *it++;
599                        if (it == end) throw SyntaxException("URI encoding: two hex digits must follow percent sign", str);
600                        char lo = *it++;
601                        if (hi >= '0' && hi <= '9')
602                                c = hi - '0';
603                        else if (hi >= 'A' && hi <= 'F')
604                                c = hi - 'A' + 10;
605                        else if (hi >= 'a' && hi <= 'f')
606                                c = hi - 'a' + 10;
607                        else throw SyntaxException("URI encoding: not a hex digit");
608                        c *= 16;
609                        if (lo >= '0' && lo <= '9')
610                                c += lo - '0';
611                        else if (lo >= 'A' && lo <= 'F')
612                                c += lo - 'A' + 10;
613                        else if (lo >= 'a' && lo <= 'f')
614                                c += lo - 'a' + 10;
615                        else throw SyntaxException("URI encoding: not a hex digit");
616                }
617                decodedStr += c;
618        }
619}
620
621
622bool URI::isWellKnownPort() const
623{
624        return _port == getWellKnownPort();
625}
626
627
628unsigned short URI::getWellKnownPort() const
629{
630        if (_scheme == "ftp")
631                return 21;
632        else if (_scheme == "ssh")
633                return 22;
634        else if (_scheme == "telnet")
635                return 23;
636        else if (_scheme == "http")
637                return 80;
638        else if (_scheme == "nntp")
639                return 119;
640        else if (_scheme == "ldap")
641                return 389;
642        else if (_scheme == "https")
643                return 443;
644        else
645                return 0;
646}
647
648
649void URI::parse(const std::string& uri)
650{
651        std::string::const_iterator it  = uri.begin();
652        std::string::const_iterator end = uri.end();
653        if (it == end) return;
654        if (*it != '/' && *it != '.' && *it != '?' && *it != '#')
655        {
656                std::string scheme;
657                while (it != end && *it != ':' && *it != '?' && *it != '#' && *it != '/') scheme += *it++;
658                if (it != end && *it == ':')
659                {
660                        ++it;
661                        if (it == end) throw SyntaxException("URI scheme must be followed by authority or path", uri);
662                        setScheme(scheme);
663                        if (*it == '/')
664                        {
665                                ++it;
666                                if (it != end && *it == '/')
667                                {
668                                        ++it;
669                                        parseAuthority(it, end);
670                                }
671                                else --it;
672                        }
673                        parsePathEtc(it, end);
674                }
675                else 
676                {
677                        it = uri.begin();
678                        parsePathEtc(it, end);
679                }
680        }
681        else parsePathEtc(it, end);
682}
683
684
685void URI::parseAuthority(std::string::const_iterator& it, const std::string::const_iterator& end)
686{
687        std::string userInfo;
688        std::string part;
689        while (it != end && *it != '/' && *it != '?' && *it != '#')
690        {
691                if (*it == '@')
692                {
693                        userInfo = part;
694                        part.clear();
695                }
696                else part += *it;
697                ++it;
698        }
699        std::string::const_iterator pbeg = part.begin();
700        std::string::const_iterator pend = part.end();
701        parseHostAndPort(pbeg, pend);
702        _userInfo = userInfo;
703}
704
705
706void URI::parseHostAndPort(std::string::const_iterator& it, const std::string::const_iterator& end)
707{
708        if (it == end) return;
709        std::string host;
710        if (*it == '[')
711        {
712                // IPv6 address
713                while (it != end && *it != ']') host += *it++;
714                if (it == end) throw SyntaxException("unterminated IPv6 address");
715                host += *it++;
716        }
717        else
718        {
719                while (it != end && *it != ':') host += *it++;
720        }
721        if (it != end && *it == ':')
722        {
723                ++it;
724                std::string port;
725                while (it != end) port += *it++;
726                if (!port.empty())
727                {
728                        int nport = 0;
729                        if (NumberParser::tryParse(port, nport) && nport > 0 && nport < 65536)
730                                _port = (unsigned short) nport;
731                        else
732                                throw SyntaxException("bad or invalid port number", port);
733                }
734                else _port = getWellKnownPort();
735        }
736        else _port = getWellKnownPort();
737        _host = host;
738        toLowerInPlace(_host);
739}
740
741
742void URI::parsePath(std::string::const_iterator& it, const std::string::const_iterator& end)
743{
744        std::string path;
745        while (it != end && *it != '?' && *it != '#') path += *it++;
746        decode(path, _path);
747}
748
749
750void URI::parsePathEtc(std::string::const_iterator& it, const std::string::const_iterator& end)
751{
752        if (it == end) return;
753        if (*it != '?' && *it != '#')
754                parsePath(it, end);
755        if (it != end && *it == '?')
756        {
757                ++it;
758                parseQuery(it, end);
759        }
760        if (it != end && *it == '#')
761        {
762                ++it;
763                parseFragment(it, end);
764        }       
765}
766
767
768void URI::parseQuery(std::string::const_iterator& it, const std::string::const_iterator& end)
769{
770        _query.clear();
771        while (it != end && *it != '#') _query += *it++;
772}
773
774
775void URI::parseFragment(std::string::const_iterator& it, const std::string::const_iterator& end)
776{
777        std::string fragment;
778        while (it != end) fragment += *it++;
779        decode(fragment, _fragment);
780}
781
782
783void URI::mergePath(const std::string& path)
784{
785        std::vector<std::string> segments;
786        std::vector<std::string> normalizedSegments;
787        bool addLeadingSlash = false;
788        if (!_path.empty())
789        {
790                getPathSegments(segments);
791                bool endsWithSlash = *(_path.rbegin()) == '/';
792                if (!endsWithSlash && !segments.empty())
793                        segments.pop_back();
794                addLeadingSlash = _path[0] == '/';
795        }
796        getPathSegments(path, segments);
797        addLeadingSlash = addLeadingSlash || (!path.empty() && path[0] == '/');
798        bool hasTrailingSlash = (!path.empty() && *(path.rbegin()) == '/');
799        bool addTrailingSlash = false;
800        for (std::vector<std::string>::const_iterator it = segments.begin(); it != segments.end(); ++it)
801        {
802                if (*it == "..")
803                {
804                        addTrailingSlash = true;
805                        if (!normalizedSegments.empty())
806                                normalizedSegments.pop_back();
807                }
808                else if (*it != ".")
809                {
810                        addTrailingSlash = false;
811                        normalizedSegments.push_back(*it);
812                }
813                else addTrailingSlash = true;
814        }
815        buildPath(normalizedSegments, addLeadingSlash, hasTrailingSlash || addTrailingSlash);
816}
817
818
819void URI::buildPath(const std::vector<std::string>& segments, bool leadingSlash, bool trailingSlash)
820{
821        _path.clear();
822        bool first = true;
823        for (std::vector<std::string>::const_iterator it = segments.begin(); it != segments.end(); ++it)
824        {
825                if (first)
826                {
827                        first = false;
828                        if (leadingSlash)
829                                _path += '/';
830                        else if (_scheme.empty() && (*it).find(':') != std::string::npos)
831                                _path.append("./");
832                }
833                else _path += '/';
834                _path.append(*it);
835        }
836        if (trailingSlash) 
837                _path += '/';
838}
839
840
841} // namespace Poco
Note: See TracBrowser for help on using the repository browser.