Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / gui / url_parser.cpp
blobb009221df36416e1728e15e4ba1150ba1570568d
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2016 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdpch.h"
21 #include "nel/misc/common.h"
22 #include "nel/gui/url_parser.h"
24 using namespace std;
26 #ifdef DEBUG_NEW
27 #define new DEBUG_NEW
28 #endif
30 namespace NLGUI
32 // ***************************************************************************
33 CUrlParser::CUrlParser(const std::string &uri)
35 parse(uri);
38 // ***************************************************************************
39 void CUrlParser::parse(std::string uri)
41 const size_t npos = std::string::npos;
42 size_t pos;
43 size_t offset = 0;
45 // strip fragment if present
46 pos = uri.find("#");
47 if (pos != npos)
49 hash = uri.substr(pos + 1);
50 uri = uri.substr(0, pos);
53 // scan for scheme
54 pos = uri.find(":");
55 if (pos != npos && pos >= 1)
57 for (uint i=0; i<pos; i++)
59 if (!isalnum(uri[i]))
61 pos = npos;
62 break;
65 if (pos != npos)
67 scheme = uri.substr(0, pos);
68 uri = uri.substr(pos + 1);
72 // scan for authority
73 if (uri.substr(0, 2) == "//")
75 pos = uri.find_first_of("/?", 2);
76 authority = uri.substr(0, pos);
77 if (pos != npos)
78 uri = uri.substr(pos);
79 else
80 uri.clear();
82 // strip empty port from authority
83 if (authority.find_last_of(":") == authority.length() - 1)
84 authority = authority.substr(0, authority.length() - 1);
86 // extract host from user:pass@host:port
87 pos = authority.find("@");
88 if (pos != npos)
89 host = authority.substr(pos + 1);
90 else
91 host = authority.substr(2);
93 // case-insensitive
94 host = NLMISC::toLowerAscii(host);
96 pos = host.find(":");
97 if (pos != npos)
98 host = host.substr(0, pos);
101 // scan for query
102 pos = uri.find("?");
103 if (pos != npos)
105 query = uri.substr(pos);
106 uri = uri.substr(0, pos);
109 // all that is remaining is path
110 path = uri;
113 void CUrlParser::inherit(const std::string &url)
115 // we have scheme, so we already absolute url
116 if (!scheme.empty())
117 return;
119 const size_t npos = std::string::npos;
120 size_t pos;
122 CUrlParser base(url);
124 scheme = base.scheme;
126 // if we already have authority, then ignore base path
127 if (!authority.empty())
128 return;
130 authority = base.authority;
131 if (path.empty())
133 path = base.path;
134 if (query.empty())
135 query = base.query;
137 else
138 if (path[0] != '/')
140 // find start of last path segment from base path
141 // if not found, then dont inherit base path at all
142 pos = base.path.find_last_of("/");
143 if (pos != npos)
144 path = base.path.substr(0, pos) + "/" + path;
147 resolveRelativePath(path);
150 void CUrlParser::resolveRelativePath(std::string &path)
152 const size_t npos = std::string::npos;
154 // no relative components in path. filename.ext is also matched, but that's fine
155 size_t pos = path.find(".");
156 if (pos == npos)
157 return;
159 // normalize path
160 size_t lhp = 0;
161 while(pos < path.size())
163 if (path[pos] == '.')
165 // scan ahead to see what we have
166 std::string sub = path.substr(pos, 2);
167 if (sub == "./" || sub == ".")
169 // starts with
170 if (pos == 0)
171 path.replace(pos, sub.size(), "/");
172 else
174 // full or last segment
175 sub = path.substr(pos-1, 3);
176 if (sub == "/./" || sub == "/.")
178 path.replace(pos, sub.size()-1, "");
179 // we just removed char that pos was pointing, so rewind
180 pos--;
184 else
185 if (sub == "..")
187 // starts with
188 if (pos == 0 && path.substr(pos, 3) == "../")
189 path.replace(pos, 3, "/");
190 else
191 if (pos > 0)
193 // full or last segment
194 sub = path.substr(pos-1, 4);
195 if (sub == "/../" || sub == "/..")
197 if (pos > 1)
198 lhp = path.find_last_of("/", pos - 2);
199 else
200 lhp = 0;
202 // pos points to first dot in ..
203 // lhp points to start slash (/) of last segment
204 pos += sub.size() - 1;
205 path.replace(lhp, pos - lhp, "/");
206 pos = lhp;
209 }// sub == ".."
210 } // path[pos] == '.'
211 pos++;
212 }// while
215 bool CUrlParser::isAbsolute() const
217 return !scheme.empty() && !authority.empty();
220 // serialize URL back to string
221 std::string CUrlParser::toString() const
223 std::string result;
224 if (!scheme.empty())
225 result += scheme + ":";
227 if (!authority.empty())
229 result += authority;
232 // path already has leading slash
233 if (!path.empty())
235 result += path;
238 if (!query.empty())
240 if (query.find_first_of("?") != 0) result += "?";
242 result += query;
245 if (!hash.empty())
247 result += "#" + hash;
250 return result;
253 }// namespace