1 //
2 // URI.cpp
3 //
4 // Library: Foundation
5 // Package: URI
6 // Module: URI
7 //
8 // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
9 // and Contributors.
10 //
11 // SPDX-License-Identifier: BSL-1.0
12 //
13
14
15 #include "Poco/URI.h"
16 #include "Poco/NumberFormatter.h"
17 #include "Poco/Exception.h"
18 #include "Poco/String.h"
19 #include "Poco/NumberParser.h"
20 #include "Poco/Path.h"
21
22
23 namespace Poco {
24
25
26 const std::string URI::RESERVED_PATH = "?#";
27 const std::string URI::RESERVED_QUERY = "?#/:;+@";
28 const std::string URI::RESERVED_QUERY_PARAM = "?#/:;+@&=";
29 const std::string URI::RESERVED_FRAGMENT = "";
30 const std::string URI::ILLEGAL = "%<>{}|\\\"^`!*'()$,[]";
31
32
URI()33 URI::URI():
34 _port(0)
35 {
36 }
37
38
URI(const std::string & uri)39 URI::URI(const std::string& uri):
40 _port(0)
41 {
42 parse(uri);
43 }
44
45
URI(const char * uri)46 URI::URI(const char* uri):
47 _port(0)
48 {
49 parse(std::string(uri));
50 }
51
52
URI(const std::string & scheme,const std::string & pathEtc)53 URI::URI(const std::string& scheme, const std::string& pathEtc):
54 _scheme(scheme),
55 _port(0)
56 {
57 toLowerInPlace(_scheme);
58 std::string::const_iterator beg = pathEtc.begin();
59 std::string::const_iterator end = pathEtc.end();
60 parsePathEtc(beg, end);
61 }
62
63
URI(const std::string & scheme,const std::string & authority,const std::string & pathEtc)64 URI::URI(const std::string& scheme, const std::string& authority, const std::string& pathEtc):
65 _scheme(scheme)
66 {
67 toLowerInPlace(_scheme);
68 std::string::const_iterator beg = authority.begin();
69 std::string::const_iterator end = authority.end();
70 parseAuthority(beg, end);
71 beg = pathEtc.begin();
72 end = pathEtc.end();
73 parsePathEtc(beg, end);
74 }
75
76
URI(const std::string & scheme,const std::string & authority,const std::string & path,const std::string & query)77 URI::URI(const std::string& scheme, const std::string& authority, const std::string& path, const std::string& query):
78 _scheme(scheme),
79 _path(path),
80 _query(query)
81 {
82 toLowerInPlace(_scheme);
83 std::string::const_iterator beg = authority.begin();
84 std::string::const_iterator end = authority.end();
85 parseAuthority(beg, end);
86 }
87
88
URI(const std::string & scheme,const std::string & authority,const std::string & path,const std::string & query,const std::string & fragment)89 URI::URI(const std::string& scheme, const std::string& authority, const std::string& path, const std::string& query, const std::string& fragment):
90 _scheme(scheme),
91 _path(path),
92 _query(query),
93 _fragment(fragment)
94 {
95 toLowerInPlace(_scheme);
96 std::string::const_iterator beg = authority.begin();
97 std::string::const_iterator end = authority.end();
98 parseAuthority(beg, end);
99 }
100
101
URI(const URI & uri)102 URI::URI(const URI& uri):
103 _scheme(uri._scheme),
104 _userInfo(uri._userInfo),
105 _host(uri._host),
106 _port(uri._port),
107 _path(uri._path),
108 _query(uri._query),
109 _fragment(uri._fragment)
110 {
111 }
112
113
URI(URI && uri)114 URI::URI(URI&& uri) noexcept:
115 _scheme(std::move(uri._scheme)),
116 _userInfo(std::move(uri._userInfo)),
117 _host(std::move(uri._host)),
118 _port(std::move(uri._port)),
119 _path(std::move(uri._path)),
120 _query(std::move(uri._query)),
121 _fragment(std::move(uri._fragment))
122 {
123 }
124
125
URI(const URI & baseURI,const std::string & relativeURI)126 URI::URI(const URI& baseURI, const std::string& relativeURI):
127 _scheme(baseURI._scheme),
128 _userInfo(baseURI._userInfo),
129 _host(baseURI._host),
130 _port(baseURI._port),
131 _path(baseURI._path),
132 _query(baseURI._query),
133 _fragment(baseURI._fragment)
134 {
135 resolve(relativeURI);
136 }
137
138
URI(const Path & path)139 URI::URI(const Path& path):
140 _scheme("file"),
141 _port(0)
142 {
143 Path absolutePath(path);
144 absolutePath.makeAbsolute();
145 _path = absolutePath.toString(Path::PATH_UNIX);
146 }
147
148
~URI()149 URI::~URI()
150 {
151 }
152
153
operator =(const URI & uri)154 URI& URI::operator = (const URI& uri)
155 {
156 if (&uri != this)
157 {
158 _scheme = uri._scheme;
159 _userInfo = uri._userInfo;
160 _host = uri._host;
161 _port = uri._port;
162 _path = uri._path;
163 _query = uri._query;
164 _fragment = uri._fragment;
165 }
166 return *this;
167 }
168
169
operator =(URI && uri)170 URI& URI::operator = (URI&& uri) noexcept
171 {
172 _scheme = std::move(uri._scheme);
173 _userInfo = std::move(uri._userInfo);
174 _host = std::move(uri._host);
175 _port = std::move(uri._port);
176 _path = std::move(uri._path);
177 _query = std::move(uri._query);
178 _fragment = std::move(uri._fragment);
179
180 return *this;
181 }
182
183
operator =(const std::string & uri)184 URI& URI::operator = (const std::string& uri)
185 {
186 clear();
187 parse(uri);
188 return *this;
189 }
190
191
operator =(const char * uri)192 URI& URI::operator = (const char* uri)
193 {
194 clear();
195 parse(std::string(uri));
196 return *this;
197 }
198
199
swap(URI & uri)200 void URI::swap(URI& uri)
201 {
202 std::swap(_scheme, uri._scheme);
203 std::swap(_userInfo, uri._userInfo);
204 std::swap(_host, uri._host);
205 std::swap(_port, uri._port);
206 std::swap(_path, uri._path);
207 std::swap(_query, uri._query);
208 std::swap(_fragment, uri._fragment);
209 }
210
211
clear()212 void URI::clear()
213 {
214 _scheme.clear();
215 _userInfo.clear();
216 _host.clear();
217 _port = 0;
218 _path.clear();
219 _query.clear();
220 _fragment.clear();
221 }
222
223
toString() const224 std::string URI::toString() const
225 {
226 std::string uri;
227 if (isRelative())
228 {
229 encode(_path, RESERVED_PATH, uri);
230 }
231 else
232 {
233 uri = _scheme;
234 uri += ':';
235 std::string auth = getAuthority();
236 if (!auth.empty() || _scheme == "file")
237 {
238 uri.append("//");
239 uri.append(auth);
240 }
241 if (!_path.empty())
242 {
243 if (!auth.empty() && _path[0] != '/')
244 uri += '/';
245 encode(_path, RESERVED_PATH, uri);
246 }
247 else if (!_query.empty() || !_fragment.empty())
248 {
249 uri += '/';
250 }
251 }
252 if (!_query.empty())
253 {
254 uri += '?';
255 uri.append(_query);
256 }
257 if (!_fragment.empty())
258 {
259 uri += '#';
260 encode(_fragment, RESERVED_FRAGMENT, uri);
261 }
262 return uri;
263 }
264
265
setScheme(const std::string & scheme)266 void URI::setScheme(const std::string& scheme)
267 {
268 _scheme = scheme;
269 toLowerInPlace(_scheme);
270 }
271
272
setUserInfo(const std::string & userInfo)273 void URI::setUserInfo(const std::string& userInfo)
274 {
275 _userInfo.clear();
276 decode(userInfo, _userInfo);
277 }
278
279
setHost(const std::string & host)280 void URI::setHost(const std::string& host)
281 {
282 _host = host;
283 }
284
285
getPort() const286 unsigned short URI::getPort() const
287 {
288 if (_port == 0)
289 return getWellKnownPort();
290 else
291 return _port;
292 }
293
294
setPort(unsigned short port)295 void URI::setPort(unsigned short port)
296 {
297 _port = port;
298 }
299
300
getAuthority() const301 std::string URI::getAuthority() const
302 {
303 std::string auth;
304 if (!_userInfo.empty())
305 {
306 auth.append(_userInfo);
307 auth += '@';
308 }
309 if (_host.find(':') != std::string::npos)
310 {
311 auth += '[';
312 auth += _host;
313 auth += ']';
314 }
315 else auth.append(_host);
316 if (_port && !isWellKnownPort())
317 {
318 auth += ':';
319 NumberFormatter::append(auth, _port);
320 }
321 return auth;
322 }
323
324
setAuthority(const std::string & authority)325 void URI::setAuthority(const std::string& authority)
326 {
327 _userInfo.clear();
328 _host.clear();
329 _port = 0;
330 std::string::const_iterator beg = authority.begin();
331 std::string::const_iterator end = authority.end();
332 parseAuthority(beg, end);
333 }
334
335
setPath(const std::string & path)336 void URI::setPath(const std::string& path)
337 {
338 _path.clear();
339 decode(path, _path);
340 }
341
342
setRawQuery(const std::string & query)343 void URI::setRawQuery(const std::string& query)
344 {
345 _query = query;
346 }
347
348
setQuery(const std::string & query)349 void URI::setQuery(const std::string& query)
350 {
351 _query.clear();
352 encode(query, RESERVED_QUERY, _query);
353 }
354
355
addQueryParameter(const std::string & param,const std::string & val)356 void URI::addQueryParameter(const std::string& param, const std::string& val)
357 {
358 if (!_query.empty()) _query += '&';
359 encode(param, RESERVED_QUERY_PARAM, _query);
360 _query += '=';
361 encode(val, RESERVED_QUERY_PARAM, _query);
362 }
363
364
getQuery() const365 std::string URI::getQuery() const
366 {
367 std::string query;
368 decode(_query, query);
369 return query;
370 }
371
372
getQueryParameters() const373 URI::QueryParameters URI::getQueryParameters() const
374 {
375 QueryParameters result;
376 std::string::const_iterator it(_query.begin());
377 std::string::const_iterator end(_query.end());
378 while (it != end)
379 {
380 std::string name;
381 std::string value;
382 while (it != end && *it != '=' && *it != '&')
383 {
384 if (*it == '+')
385 name += ' ';
386 else
387 name += *it;
388 ++it;
389 }
390 if (it != end && *it == '=')
391 {
392 ++it;
393 while (it != end && *it != '&')
394 {
395 if (*it == '+')
396 value += ' ';
397 else
398 value += *it;
399 ++it;
400 }
401 }
402 std::string decodedName;
403 std::string decodedValue;
404 URI::decode(name, decodedName);
405 URI::decode(value, decodedValue);
406 result.push_back(std::make_pair(decodedName, decodedValue));
407 if (it != end && *it == '&') ++it;
408 }
409 return result;
410 }
411
412
setQueryParameters(const QueryParameters & params)413 void URI::setQueryParameters(const QueryParameters& params)
414 {
415 _query.clear();
416 for (const auto& p: params)
417 {
418 addQueryParameter(p.first, p.second);
419 }
420 }
421
422
setFragment(const std::string & fragment)423 void URI::setFragment(const std::string& fragment)
424 {
425 _fragment.clear();
426 decode(fragment, _fragment);
427 }
428
429
setPathEtc(const std::string & pathEtc)430 void URI::setPathEtc(const std::string& pathEtc)
431 {
432 _path.clear();
433 _query.clear();
434 _fragment.clear();
435 std::string::const_iterator beg = pathEtc.begin();
436 std::string::const_iterator end = pathEtc.end();
437 parsePathEtc(beg, end);
438 }
439
440
getPathEtc() const441 std::string URI::getPathEtc() const
442 {
443 std::string pathEtc;
444 encode(_path, RESERVED_PATH, pathEtc);
445 if (!_query.empty())
446 {
447 pathEtc += '?';
448 pathEtc += _query;
449 }
450 if (!_fragment.empty())
451 {
452 pathEtc += '#';
453 encode(_fragment, RESERVED_FRAGMENT, pathEtc);
454 }
455 return pathEtc;
456 }
457
458
getPathAndQuery() const459 std::string URI::getPathAndQuery() const
460 {
461 std::string pathAndQuery;
462 encode(_path, RESERVED_PATH, pathAndQuery);
463 if (!_query.empty())
464 {
465 pathAndQuery += '?';
466 pathAndQuery += _query;
467 }
468 return pathAndQuery;
469 }
470
471
resolve(const std::string & relativeURI)472 void URI::resolve(const std::string& relativeURI)
473 {
474 URI parsedURI(relativeURI);
475 resolve(parsedURI);
476 }
477
478
resolve(const URI & relativeURI)479 void URI::resolve(const URI& relativeURI)
480 {
481 if (!relativeURI._scheme.empty())
482 {
483 _scheme = relativeURI._scheme;
484 _userInfo = relativeURI._userInfo;
485 _host = relativeURI._host;
486 _port = relativeURI._port;
487 _path = relativeURI._path;
488 _query = relativeURI._query;
489 removeDotSegments();
490 }
491 else
492 {
493 if (!relativeURI._host.empty())
494 {
495 _userInfo = relativeURI._userInfo;
496 _host = relativeURI._host;
497 _port = relativeURI._port;
498 _path = relativeURI._path;
499 _query = relativeURI._query;
500 removeDotSegments();
501 }
502 else
503 {
504 if (relativeURI._path.empty())
505 {
506 if (!relativeURI._query.empty())
507 _query = relativeURI._query;
508 }
509 else
510 {
511 if (relativeURI._path[0] == '/')
512 {
513 _path = relativeURI._path;
514 removeDotSegments();
515 }
516 else
517 {
518 mergePath(relativeURI._path);
519 }
520 _query = relativeURI._query;
521 }
522 }
523 }
524 _fragment = relativeURI._fragment;
525 }
526
527
isRelative() const528 bool URI::isRelative() const
529 {
530 return _scheme.empty();
531 }
532
533
empty() const534 bool URI::empty() const
535 {
536 return _scheme.empty() && _host.empty() && _path.empty() && _query.empty() && _fragment.empty();
537 }
538
539
operator ==(const URI & uri) const540 bool URI::operator == (const URI& uri) const
541 {
542 return equals(uri);
543 }
544
545
operator ==(const std::string & uri) const546 bool URI::operator == (const std::string& uri) const
547 {
548 URI parsedURI(uri);
549 return equals(parsedURI);
550 }
551
552
operator !=(const URI & uri) const553 bool URI::operator != (const URI& uri) const
554 {
555 return !equals(uri);
556 }
557
558
operator !=(const std::string & uri) const559 bool URI::operator != (const std::string& uri) const
560 {
561 URI parsedURI(uri);
562 return !equals(parsedURI);
563 }
564
565
equals(const URI & uri) const566 bool URI::equals(const URI& uri) const
567 {
568 return _scheme == uri._scheme
569 && _userInfo == uri._userInfo
570 && _host == uri._host
571 && getPort() == uri.getPort()
572 && _path == uri._path
573 && _query == uri._query
574 && _fragment == uri._fragment;
575 }
576
577
normalize()578 void URI::normalize()
579 {
580 removeDotSegments(!isRelative());
581 }
582
583
removeDotSegments(bool removeLeading)584 void URI::removeDotSegments(bool removeLeading)
585 {
586 if (_path.empty()) return;
587
588 bool leadingSlash = *(_path.begin()) == '/';
589 bool trailingSlash = *(_path.rbegin()) == '/';
590 std::vector<std::string> segments;
591 std::vector<std::string> normalizedSegments;
592 getPathSegments(segments);
593 for (const auto& s: segments)
594 {
595 if (s == "..")
596 {
597 if (!normalizedSegments.empty())
598 {
599 if (normalizedSegments.back() == "..")
600 normalizedSegments.push_back(s);
601 else
602 normalizedSegments.pop_back();
603 }
604 else if (!removeLeading)
605 {
606 normalizedSegments.push_back(s);
607 }
608 }
609 else if (s != ".")
610 {
611 normalizedSegments.push_back(s);
612 }
613 }
614 buildPath(normalizedSegments, leadingSlash, trailingSlash);
615 }
616
617
getPathSegments(std::vector<std::string> & segments)618 void URI::getPathSegments(std::vector<std::string>& segments)
619 {
620 getPathSegments(_path, segments);
621 }
622
623
getPathSegments(const std::string & path,std::vector<std::string> & segments)624 void URI::getPathSegments(const std::string& path, std::vector<std::string>& segments)
625 {
626 std::string::const_iterator it = path.begin();
627 std::string::const_iterator end = path.end();
628 std::string seg;
629 while (it != end)
630 {
631 if (*it == '/')
632 {
633 if (!seg.empty())
634 {
635 segments.push_back(seg);
636 seg.clear();
637 }
638 }
639 else seg += *it;
640 ++it;
641 }
642 if (!seg.empty())
643 segments.push_back(seg);
644 }
645
646
encode(const std::string & str,const std::string & reserved,std::string & encodedStr)647 void URI::encode(const std::string& str, const std::string& reserved, std::string& encodedStr)
648 {
649 for (auto c: str)
650 {
651 if ((c >= 'a' && c <= 'z') ||
652 (c >= 'A' && c <= 'Z') ||
653 (c >= '0' && c <= '9') ||
654 c == '-' || c == '_' ||
655 c == '.' || c == '~')
656 {
657 encodedStr += c;
658 }
659 else if (c <= 0x20 || c >= 0x7F || ILLEGAL.find(c) != std::string::npos || reserved.find(c) != std::string::npos)
660 {
661 encodedStr += '%';
662 encodedStr += NumberFormatter::formatHex((unsigned) (unsigned char) c, 2);
663 }
664 else encodedStr += c;
665 }
666 }
667
668
decode(const std::string & str,std::string & decodedStr,bool plusAsSpace)669 void URI::decode(const std::string& str, std::string& decodedStr, bool plusAsSpace)
670 {
671 bool inQuery = false;
672 std::string::const_iterator it = str.begin();
673 std::string::const_iterator end = str.end();
674 while (it != end)
675 {
676 char c = *it++;
677 if (c == '?') inQuery = true;
678 // spaces may be encoded as plus signs in the query
679 if (inQuery && plusAsSpace && c == '+') c = ' ';
680 else if (c == '%')
681 {
682 if (it == end) throw URISyntaxException("URI encoding: no hex digit following percent sign", str);
683 char hi = *it++;
684 if (it == end) throw URISyntaxException("URI encoding: two hex digits must follow percent sign", str);
685 char lo = *it++;
686 if (hi >= '0' && hi <= '9')
687 c = hi - '0';
688 else if (hi >= 'A' && hi <= 'F')
689 c = hi - 'A' + 10;
690 else if (hi >= 'a' && hi <= 'f')
691 c = hi - 'a' + 10;
692 else throw URISyntaxException("URI encoding: not a hex digit");
693 c *= 16;
694 if (lo >= '0' && lo <= '9')
695 c += lo - '0';
696 else if (lo >= 'A' && lo <= 'F')
697 c += lo - 'A' + 10;
698 else if (lo >= 'a' && lo <= 'f')
699 c += lo - 'a' + 10;
700 else throw URISyntaxException("URI encoding: not a hex digit");
701 }
702 decodedStr += c;
703 }
704 }
705
706
isWellKnownPort() const707 bool URI::isWellKnownPort() const
708 {
709 return _port == getWellKnownPort();
710 }
711
712
getWellKnownPort() const713 unsigned short URI::getWellKnownPort() const
714 {
715 if (_scheme == "ftp")
716 return 21;
717 else if (_scheme == "ssh")
718 return 22;
719 else if (_scheme == "telnet")
720 return 23;
721 else if (_scheme == "smtp")
722 return 25;
723 else if (_scheme == "dns")
724 return 53;
725 else if (_scheme == "http" || _scheme == "ws")
726 return 80;
727 else if (_scheme == "nntp")
728 return 119;
729 else if (_scheme == "imap")
730 return 143;
731 else if (_scheme == "ldap")
732 return 389;
733 else if (_scheme == "https" || _scheme == "wss")
734 return 443;
735 else if (_scheme == "smtps")
736 return 465;
737 else if (_scheme == "rtsp")
738 return 554;
739 else if (_scheme == "ldaps")
740 return 636;
741 else if (_scheme == "dnss")
742 return 853;
743 else if (_scheme == "imaps")
744 return 993;
745 else if (_scheme == "sip")
746 return 5060;
747 else if (_scheme == "sips")
748 return 5061;
749 else if (_scheme == "xmpp")
750 return 5222;
751 else
752 return 0;
753 }
754
755
parse(const std::string & uri)756 void URI::parse(const std::string& uri)
757 {
758 std::string::const_iterator it = uri.begin();
759 std::string::const_iterator end = uri.end();
760 if (it == end) return;
761 if (*it != '/' && *it != '.' && *it != '?' && *it != '#')
762 {
763 std::string scheme;
764 while (it != end && *it != ':' && *it != '?' && *it != '#' && *it != '/') scheme += *it++;
765 if (it != end && *it == ':')
766 {
767 ++it;
768 if (it == end) throw URISyntaxException("URI scheme must be followed by authority or path", uri);
769 setScheme(scheme);
770 if (*it == '/')
771 {
772 ++it;
773 if (it != end && *it == '/')
774 {
775 ++it;
776 parseAuthority(it, end);
777 }
778 else --it;
779 }
780 parsePathEtc(it, end);
781 }
782 else
783 {
784 it = uri.begin();
785 parsePathEtc(it, end);
786 }
787 }
788 else parsePathEtc(it, end);
789 }
790
791
parseAuthority(std::string::const_iterator & it,const std::string::const_iterator & end)792 void URI::parseAuthority(std::string::const_iterator& it, const std::string::const_iterator& end)
793 {
794 std::string userInfo;
795 std::string part;
796 while (it != end && *it != '/' && *it != '?' && *it != '#')
797 {
798 if (*it == '@')
799 {
800 userInfo = part;
801 part.clear();
802 }
803 else part += *it;
804 ++it;
805 }
806 std::string::const_iterator pbeg = part.begin();
807 std::string::const_iterator pend = part.end();
808 parseHostAndPort(pbeg, pend);
809 _userInfo = userInfo;
810 }
811
812
parseHostAndPort(std::string::const_iterator & it,const std::string::const_iterator & end)813 void URI::parseHostAndPort(std::string::const_iterator& it, const std::string::const_iterator& end)
814 {
815 if (it == end) return;
816 std::string host;
817 if (*it == '[')
818 {
819 // IPv6 address
820 ++it;
821 while (it != end && *it != ']') host += *it++;
822 if (it == end) throw URISyntaxException("unterminated IPv6 address");
823 ++it;
824 }
825 else
826 {
827 while (it != end && *it != ':') host += *it++;
828 }
829 if (it != end && *it == ':')
830 {
831 ++it;
832 std::string port;
833 while (it != end) port += *it++;
834 if (!port.empty())
835 {
836 int nport = 0;
837 if (NumberParser::tryParse(port, nport) && nport > 0 && nport < 65536)
838 _port = (unsigned short) nport;
839 else
840 throw URISyntaxException("bad or invalid port number", port);
841 }
842 else _port = 0;
843 }
844 else _port = 0;
845 _host = host;
846 toLowerInPlace(_host);
847 }
848
849
parsePath(std::string::const_iterator & it,const std::string::const_iterator & end)850 void URI::parsePath(std::string::const_iterator& it, const std::string::const_iterator& end)
851 {
852 std::string path;
853 while (it != end && *it != '?' && *it != '#') path += *it++;
854 decode(path, _path);
855 }
856
857
parsePathEtc(std::string::const_iterator & it,const std::string::const_iterator & end)858 void URI::parsePathEtc(std::string::const_iterator& it, const std::string::const_iterator& end)
859 {
860 if (it == end) return;
861 if (*it != '?' && *it != '#')
862 parsePath(it, end);
863 if (it != end && *it == '?')
864 {
865 ++it;
866 parseQuery(it, end);
867 }
868 if (it != end && *it == '#')
869 {
870 ++it;
871 parseFragment(it, end);
872 }
873 }
874
875
parseQuery(std::string::const_iterator & it,const std::string::const_iterator & end)876 void URI::parseQuery(std::string::const_iterator& it, const std::string::const_iterator& end)
877 {
878 _query.clear();
879 while (it != end && *it != '#') _query += *it++;
880 }
881
882
parseFragment(std::string::const_iterator & it,const std::string::const_iterator & end)883 void URI::parseFragment(std::string::const_iterator& it, const std::string::const_iterator& end)
884 {
885 std::string fragment;
886 while (it != end) fragment += *it++;
887 decode(fragment, _fragment);
888 }
889
890
mergePath(const std::string & path)891 void URI::mergePath(const std::string& path)
892 {
893 std::vector<std::string> segments;
894 std::vector<std::string> normalizedSegments;
895 bool addLeadingSlash = false;
896 if (!_path.empty())
897 {
898 getPathSegments(segments);
899 bool endsWithSlash = *(_path.rbegin()) == '/';
900 if (!endsWithSlash && !segments.empty())
901 segments.pop_back();
902 addLeadingSlash = _path[0] == '/';
903 }
904 getPathSegments(path, segments);
905 addLeadingSlash = addLeadingSlash || (!path.empty() && path[0] == '/');
906 bool hasTrailingSlash = (!path.empty() && *(path.rbegin()) == '/');
907 bool addTrailingSlash = false;
908 for (const auto& s: segments)
909 {
910 if (s == "..")
911 {
912 addTrailingSlash = true;
913 if (!normalizedSegments.empty())
914 normalizedSegments.pop_back();
915 }
916 else if (s != ".")
917 {
918 addTrailingSlash = false;
919 normalizedSegments.push_back(s);
920 }
921 else addTrailingSlash = true;
922 }
923 buildPath(normalizedSegments, addLeadingSlash, hasTrailingSlash || addTrailingSlash);
924 }
925
926
buildPath(const std::vector<std::string> & segments,bool leadingSlash,bool trailingSlash)927 void URI::buildPath(const std::vector<std::string>& segments, bool leadingSlash, bool trailingSlash)
928 {
929 _path.clear();
930 bool first = true;
931 for (const auto& s: segments)
932 {
933 if (first)
934 {
935 first = false;
936 if (leadingSlash)
937 _path += '/';
938 else if (_scheme.empty() && s.find(':') != std::string::npos)
939 _path.append("./");
940 }
941 else _path += '/';
942 _path.append(s);
943 }
944 if (trailingSlash)
945 _path += '/';
946 }
947
948
949 } // namespace Poco
950