1 -
//
 
2 -
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
 
3 -
//
 
4 -
// Distributed under the Boost Software License, Version 1.0. (See accompanying
 
5 -
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 
6 -
//
 
7 -
// Official repository: https://github.com/boostorg/url
 
8 -
//
 
9 -

 
10 -

 
11 -
#include <boost/url/detail/config.hpp>
 
12 -
#include <boost/url/authority_view.hpp>
 
13 -
#include "detail/normalize.hpp"
 
14 -
#include <boost/url/grammar/parse.hpp>
 
15 -
#include <boost/url/rfc/authority_rule.hpp>
 
16 -
#include <boost/url/rfc/pct_encoded_rule.hpp>
 
17 -
#include <array>
 
18 -
#include <ostream>
 
19 -

 
20 -
namespace boost {
 
21 -
namespace urls {
 
22 -

 
23 -
//------------------------------------------------
 
24 -

 
25 -
namespace detail {
 
26 -

 
27 -
authority_view
 
28 -
url_impl::
 
29 -
construct_authority() const noexcept
 
30 -
{
 
31 -
    return authority_view(*this);
 
32 -
}
 
33 -

 
34 -
} // detail
 
35 -

 
36 -
//------------------------------------------------
 
37 -

 
38 -
authority_view::
 
39 -
authority_view(
 
40 -
    detail::url_impl const& u) noexcept
 
41 -
    : u_(u)
 
42 -
{
 
43 -
}
 
44 -

 
45 -
//------------------------------------------------
 
46 -

 
47 -
authority_view::
 
48 -
~authority_view()
 
49 -
{
 
50 -
}
 
51 -

 
52 -
authority_view::
 
53 -
authority_view() noexcept
 
54 -
    : u_(from::authority)
 
55 -
{
 
56 -
}
 
57 -

 
58 -
authority_view::
 
59 -
authority_view(
 
60 -
    core::string_view s)
 
61 -
    : authority_view(
 
62 -
        parse_authority(s
 
63 -
            ).value(BOOST_URL_POS))
 
64 -
{
 
65 -
}
 
66 -

 
67 -
authority_view::
 
68 -
authority_view(
 
69 -
    authority_view const&) noexcept = default;
 
70 -

 
71 -
authority_view&
 
72 -
authority_view::
 
73 -
operator=(
 
74 -
    authority_view const&) noexcept = default;
 
75 -

 
76 -
//------------------------------------------------
 
77 -
//
 
78 -
// Userinfo
 
79 -
//
 
80 -
//------------------------------------------------
 
81 -

 
82 -
bool
 
83 -
authority_view::
 
84 -
has_userinfo() const noexcept
 
85 -
{
 
86 -
    auto n = u_.len(id_pass);
 
87 -
    if(n == 0)
 
88 -
        return false;
 
89 -
    BOOST_ASSERT(u_.get(
 
90 -
        id_pass).ends_with('@'));
 
91 -
    return true;
 
92 -
}
 
93 -

 
94 -
pct_string_view
 
95 -
authority_view::
 
96 -
encoded_userinfo() const noexcept
 
97 -
{
 
98 -
    auto s = u_.get(
 
99 -
        id_user, id_host);
 
100 -
    if(s.empty())
 
101 -
        return s;
 
102 -
    BOOST_ASSERT(
 
103 -
        s.ends_with('@'));
 
104 -
    s.remove_suffix(1);
 
105 -
    return make_pct_string_view_unsafe(
 
106 -
        s.data(),
 
107 -
        s.size(),
 
108 -
        u_.decoded_[id_user] +
 
109 -
            u_.decoded_[id_pass] +
 
110 -
            has_password());
 
111 -
}
 
112 -

 
113 -
pct_string_view
 
114 -
authority_view::
 
115 -
encoded_user() const noexcept
 
116 -
{
 
117 -
    auto s = u_.get(id_user);
 
118 -
    return make_pct_string_view_unsafe(
 
119 -
        s.data(),
 
120 -
        s.size(),
 
121 -
        u_.decoded_[id_user]);
 
122 -
}
 
123 -

 
124 -
bool
 
125 -
authority_view::
 
126 -
has_password() const noexcept
 
127 -
{
 
128 -
    auto const n = u_.len(id_pass);
 
129 -
    if(n > 1)
 
130 -
    {
 
131 -
        BOOST_ASSERT(u_.get(id_pass
 
132 -
            ).starts_with(':'));
 
133 -
        BOOST_ASSERT(u_.get(id_pass
 
134 -
            ).ends_with('@'));
 
135 -
        return true;
 
136 -
    }
 
137 -
    BOOST_ASSERT(n == 0 || u_.get(
 
138 -
        id_pass).ends_with('@'));
 
139 -
    return false;
 
140 -
}
 
141 -

 
142 -
pct_string_view
 
143 -
authority_view::
 
144 -
encoded_password() const noexcept
 
145 -
{
 
146 -
    auto s = u_.get(id_pass);
 
147 -
    switch(s.size())
 
148 -
    {
 
149 -
    case 1:
 
150 -
        BOOST_ASSERT(
 
151 -
            s.starts_with('@'));
 
152 -
        s.remove_prefix(1);
 
153 -
        BOOST_FALLTHROUGH;
 
154 -
    case 0:
 
155 -
        return make_pct_string_view_unsafe(
 
156 -
            s.data(), s.size(), 0);
 
157 -
    default:
 
158 -
        break;
 
159 -
    }
 
160 -
    BOOST_ASSERT(s.ends_with('@'));
 
161 -
    BOOST_ASSERT(s.starts_with(':'));
 
162 -
    return make_pct_string_view_unsafe(
 
163 -
        s.data() + 1,
 
164 -
        s.size() - 2,
 
165 -
        u_.decoded_[id_pass]);
 
166 -
}
 
167 -

 
168 -
//------------------------------------------------
 
169 -
//
 
170 -
// Host
 
171 -
//
 
172 -
//------------------------------------------------
 
173 -
/*
 
174 -
host_type       host_type()                 // ipv4, ipv6, ipvfuture, name
 
175 -

 
176 -
std::string     host()                      // return encoded_host().decode()
 
177 -
pct_string_view encoded_host()              // return host part, as-is
 
178 -
std::string     host_address()              // return encoded_host_address().decode()
 
179 -
pct_string_view encoded_host_address()      // ipv4, ipv6, ipvfut, or encoded name, no brackets
 
180 -

 
181 -
ipv4_address    host_ipv4_address()         // return ipv4_address or {}
 
182 -
ipv6_address    host_ipv6_address()         // return ipv6_address or {}
 
183 -
core::string_view     host_ipvfuture()            // return ipvfuture or {}
 
184 -
std::string     host_name()                 // return decoded name or ""
 
185 -
pct_string_view encoded_host_name()         // return encoded host name or ""
 
186 -
*/
 
187 -

 
188 -
pct_string_view
 
189 -
authority_view::
 
190 -
encoded_host() const noexcept
 
191 -
{
 
192 -
    return u_.pct_get(id_host);
 
193 -
}
 
194 -

 
195 -
pct_string_view
 
196 -
authority_view::
 
197 -
encoded_host_address() const noexcept
 
198 -
{
 
199 -
    core::string_view s = u_.get(id_host);
 
200 -
    std::size_t n;
 
201 -
    switch(u_.host_type_)
 
202 -
    {
 
203 -
    case urls::host_type::name:
 
204 -
    case urls::host_type::ipv4:
 
205 -
        n = u_.decoded_[id_host];
 
206 -
        break;
 
207 -

 
208 -
    case urls::host_type::ipv6:
 
209 -
    case urls::host_type::ipvfuture:
 
210 -
    {
 
211 -
        BOOST_ASSERT(
 
212 -
            u_.decoded_[id_host] ==
 
213 -
                s.size());
 
214 -
        BOOST_ASSERT(s.size() >= 2);
 
215 -
        BOOST_ASSERT(s.front() == '[');
 
216 -
        BOOST_ASSERT(s.back() == ']');
 
217 -
        s = s.substr(1, s.size() - 2);
 
218 -
        n = u_.decoded_[id_host] - 2;
 
219 -
        break;
 
220 -
    }
 
221 -
    // LCOV_EXCL_START
 
222 -
    default:
 
223 -
    case urls::host_type::none:
 
224 -
        /*
 
225 -
         * This condition is for correctness
 
226 -
         * only.
 
227 -
         * This should never happen, because
 
228 -
         * the `host_rule` will set the host
 
229 -
         * type to `name` when it's empty.
 
230 -
         * This is correct because `reg-name`
 
231 -
         * accepts empty strings.
 
232 -
         */
 
233 -
        BOOST_ASSERT(s.empty());
 
234 -
        n = 0;
 
235 -
        break;
 
236 -
    // LCOV_EXCL_STOP
 
237 -
    }
 
238 -
    return make_pct_string_view_unsafe(
 
239 -
        s.data(), s.size(), n);
 
240 -
}
 
241 -

 
242 -
urls::ipv4_address
 
243 -
authority_view::
 
244 -
host_ipv4_address() const noexcept
 
245 -
{
 
246 -
    if(u_.host_type_ !=
 
247 -
            urls::host_type::ipv4)
 
248 -
        return {};
 
249 -
    ipv4_address::bytes_type b{{}};
 
250 -
    std::memcpy(
 
251 -
        &b[0], &u_.ip_addr_[0], b.size());
 
252 -
    return urls::ipv4_address(b);
 
253 -
}
 
254 -

 
255 -
urls::ipv6_address
 
256 -
authority_view::
 
257 -
host_ipv6_address() const noexcept
 
258 -
{
 
259 -
    if(u_.host_type_ !=
 
260 -
            urls::host_type::ipv6)
 
261 -
        return {};
 
262 -
    ipv6_address::bytes_type b{{}};
 
263 -
    std::memcpy(
 
264 -
        &b[0], &u_.ip_addr_[0], b.size());
 
265 -
    return urls::ipv6_address(b);
 
266 -
}
 
267 -

 
268 -
core::string_view
 
269 -
authority_view::
 
270 -
host_ipvfuture() const noexcept
 
271 -
{
 
272 -
    if(u_.host_type_ !=
 
273 -
            urls::host_type::ipvfuture)
 
274 -
        return {};
 
275 -
    core::string_view s = u_.get(id_host);
 
276 -
    BOOST_ASSERT(s.size() >= 6);
 
277 -
    BOOST_ASSERT(s.front() == '[');
 
278 -
    BOOST_ASSERT(s.back() == ']');
 
279 -
    s = s.substr(1, s.size() - 2);
 
280 -
    return s;
 
281 -
}
 
282 -

 
283 -
pct_string_view
 
284 -
authority_view::
 
285 -
encoded_host_name() const noexcept
 
286 -
{
 
287 -
    if(u_.host_type_ !=
 
288 -
            urls::host_type::name)
 
289 -
        return {};
 
290 -
    return u_.pct_get(id_host);
 
291 -
}
 
292 -

 
293 -
//------------------------------------------------
 
294 -
//
 
295 -
// Port
 
296 -
//
 
297 -
//------------------------------------------------
 
298 -

 
299 -
bool
 
300 -
authority_view::
 
301 -
has_port() const noexcept
 
302 -
{
 
303 -
    auto const n = u_.len(id_port);
 
304 -
    if(n == 0)
 
305 -
        return false;
 
306 -
    BOOST_ASSERT(
 
307 -
        u_.get(id_port).starts_with(':'));
 
308 -
    return true;
 
309 -
}
 
310 -

 
311 -
core::string_view
 
312 -
authority_view::
 
313 -
port() const noexcept
 
314 -
{
 
315 -
    auto s = u_.get(id_port);
 
316 -
    if(s.empty())
 
317 -
        return s;
 
318 -
    BOOST_ASSERT(has_port());
 
319 -
    return s.substr(1);
 
320 -
}
 
321 -

 
322 -
std::uint16_t
 
323 -
authority_view::
 
324 -
port_number() const noexcept
 
325 -
{
 
326 -
    BOOST_ASSERT(
 
327 -
        has_port() ||
 
328 -
        u_.port_number_ == 0);
 
329 -
    return u_.port_number_;
 
330 -
}
 
331 -

 
332 -
pct_string_view
 
333 -
authority_view::
 
334 -
encoded_host_and_port() const noexcept
 
335 -
{
 
336 -
    return u_.get(id_host, id_end);
 
337 -
}
 
338 -

 
339 -
//------------------------------------------------
 
340 -
//
 
341 -
// Parsing
 
342 -
//
 
343 -
//------------------------------------------------
 
344 -

 
345 -
system::result<authority_view>
 
346 -
parse_authority(
 
347 -
    core::string_view s) noexcept
 
348 -
{
 
349 -
    return grammar::parse(s, authority_rule);
 
350 -
}
 
351 -

 
352 -
//------------------------------------------------
 
353 -
//
 
354 -
// Comparisons
 
355 -
//
 
356 -
//------------------------------------------------
 
357 -

 
358 -
int
 
359 -
authority_view::
 
360 -
compare(const authority_view& other) const noexcept
 
361 -
{
 
362 -
    auto comp = static_cast<int>(has_userinfo()) -
 
363 -
        static_cast<int>(other.has_userinfo());
 
364 -
    if ( comp != 0 )
 
365 -
        return comp;
 
366 -

 
367 -
    if (has_userinfo())
 
368 -
    {
 
369 -
        comp = detail::compare_encoded(
 
370 -
            encoded_user(),
 
371 -
            other.encoded_user());
 
372 -
        if ( comp != 0 )
 
373 -
            return comp;
 
374 -

 
375 -
        comp = static_cast<int>(has_password()) -
 
376 -
               static_cast<int>(other.has_password());
 
377 -
        if ( comp != 0 )
 
378 -
            return comp;
 
379 -

 
380 -
        if (has_password())
 
381 -
        {
 
382 -
            comp = detail::compare_encoded(
 
383 -
                encoded_password(),
 
384 -
                other.encoded_password());
 
385 -
            if ( comp != 0 )
 
386 -
                return comp;
 
387 -
        }
 
388 -
    }
 
389 -

 
390 -
    comp = detail::ci_compare_encoded(
 
391 -
        encoded_host(),
 
392 -
        other.encoded_host());
 
393 -
    if ( comp != 0 )
 
394 -
        return comp;
 
395 -

 
396 -
    comp = static_cast<int>(has_port()) -
 
397 -
           static_cast<int>(other.has_port());
 
398 -
    if ( comp != 0 )
 
399 -
        return comp;
 
400 -

 
401 -
    if (has_port())
 
402 -
    {
 
403 -
        comp = detail::compare(
 
404 -
            port(),
 
405 -
            other.port());
 
406 -
        if ( comp != 0 )
 
407 -
            return comp;
 
408 -
    }
 
409 -

 
410 -
    return 0;
 
411 -
}
 
412 -

 
413 -
} // urls
 
414 -
} // boost
 
415 -