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