/src/libreoffice/stoc/source/uriproc/UriReferenceFactory.cxx
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include <sal/config.h> |
21 | | |
22 | | #include <algorithm> |
23 | | #include <cassert> |
24 | | #include <cstddef> |
25 | | #include <string_view> |
26 | | #include <utility> |
27 | | #include <vector> |
28 | | |
29 | | #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> |
30 | | #include <com/sun/star/lang/XMultiComponentFactory.hpp> |
31 | | #include <com/sun/star/lang/XServiceInfo.hpp> |
32 | | #include <com/sun/star/uno/Any.hxx> |
33 | | #include <com/sun/star/uno/Exception.hpp> |
34 | | #include <com/sun/star/uno/Reference.hxx> |
35 | | #include <com/sun/star/uno/RuntimeException.hpp> |
36 | | #include <com/sun/star/uno/Sequence.hxx> |
37 | | #include <com/sun/star/uno/XComponentContext.hpp> |
38 | | #include <com/sun/star/uno/XInterface.hpp> |
39 | | #include <com/sun/star/uri/RelativeUriExcessParentSegments.hpp> |
40 | | #include <com/sun/star/uri/XUriReference.hpp> |
41 | | #include <com/sun/star/uri/XUriReferenceFactory.hpp> |
42 | | #include <com/sun/star/uri/XUriSchemeParser.hpp> |
43 | | #include <cppuhelper/exc_hlp.hxx> |
44 | | #include <cppuhelper/implbase.hxx> |
45 | | #include <cppuhelper/supportsservice.hxx> |
46 | | #include <cppuhelper/weak.hxx> |
47 | | #include <rtl/character.hxx> |
48 | | #include <rtl/ustrbuf.hxx> |
49 | | #include <rtl/ustring.hxx> |
50 | | #include <sal/types.h> |
51 | | |
52 | | #include "UriReference.hxx" |
53 | | |
54 | | namespace { |
55 | | |
56 | 0 | bool equalIgnoreEscapeCase(std::u16string_view s1, std::u16string_view s2) { |
57 | 0 | if (s1.size() == s2.size()) { |
58 | 0 | for (size_t i = 0; i < s1.size();) { |
59 | 0 | if (s1[i] == '%' && s2[i] == '%' && s1.size() - i > 2 |
60 | 0 | && rtl::isAsciiHexDigit(s1[i + 1]) |
61 | 0 | && rtl::isAsciiHexDigit(s1[i + 2]) |
62 | 0 | && rtl::isAsciiHexDigit(s2[i + 1]) |
63 | 0 | && rtl::isAsciiHexDigit(s2[i + 2]) |
64 | 0 | && rtl::compareIgnoreAsciiCase(s1[i + 1], s2[i + 1]) == 0 |
65 | 0 | && rtl::compareIgnoreAsciiCase(s1[i + 2], s2[i + 2]) == 0) |
66 | 0 | { |
67 | 0 | i += 3; |
68 | 0 | } else if (s1[i] != s2[i]) { |
69 | 0 | return false; |
70 | 0 | } else { |
71 | 0 | ++i; |
72 | 0 | } |
73 | 0 | } |
74 | 0 | return true; |
75 | 0 | } else { |
76 | 0 | return false; |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | 152k | sal_Int32 parseScheme(std::u16string_view uriReference) { |
81 | 152k | if (uriReference.size() >= 2 && rtl::isAsciiAlpha(uriReference[0])) { |
82 | 934k | for (size_t i = 0; i < uriReference.size(); ++i) { |
83 | 915k | sal_Unicode c = uriReference[i]; |
84 | 915k | if (c == ':') { |
85 | 105k | return i; |
86 | 809k | } else if (!rtl::isAsciiAlpha(c) && !rtl::isAsciiDigit(c) |
87 | 809k | && c != '+' && c != '-' && c != '.') |
88 | 6.95k | { |
89 | 6.95k | break; |
90 | 6.95k | } |
91 | 915k | } |
92 | 131k | } |
93 | 46.3k | return -1; |
94 | 152k | } |
95 | | |
96 | | class UriReference: |
97 | | public cppu::WeakImplHelper<css::uri::XUriReference> |
98 | | { |
99 | | public: |
100 | | UriReference( |
101 | | OUString const & scheme, bool bHasAuthority, |
102 | | OUString const & authority, OUString const & path, |
103 | | bool bHasQuery, OUString const & query): |
104 | 152k | m_base( |
105 | 152k | scheme, bHasAuthority, authority, path, bHasQuery, |
106 | 152k | query) |
107 | 152k | {} |
108 | | |
109 | | UriReference(const UriReference&) = delete; |
110 | | UriReference& operator=(const UriReference&) = delete; |
111 | | |
112 | | virtual OUString SAL_CALL getUriReference() override |
113 | 43.7k | { return m_base.getUriReference(); } |
114 | | |
115 | | virtual sal_Bool SAL_CALL isAbsolute() override |
116 | 55.0k | { return m_base.isAbsolute(); } |
117 | | |
118 | | virtual OUString SAL_CALL getScheme() override |
119 | 27.5k | { return m_base.getScheme(); } |
120 | | |
121 | | virtual OUString SAL_CALL getSchemeSpecificPart() override |
122 | 0 | { return m_base.getSchemeSpecificPart(); } |
123 | | |
124 | | virtual sal_Bool SAL_CALL isHierarchical() override |
125 | 0 | { return m_base.isHierarchical(); } |
126 | | |
127 | | virtual sal_Bool SAL_CALL hasAuthority() override |
128 | 82.5k | { return m_base.hasAuthority(); } |
129 | | |
130 | | virtual OUString SAL_CALL getAuthority() override |
131 | 27.5k | { return m_base.getAuthority(); } |
132 | | |
133 | | virtual OUString SAL_CALL getPath() override |
134 | 110k | { return m_base.getPath(); } |
135 | | |
136 | | virtual sal_Bool SAL_CALL hasRelativePath() override |
137 | 27.5k | { return m_base.hasRelativePath(); } |
138 | | |
139 | | virtual sal_Int32 SAL_CALL getPathSegmentCount() override |
140 | 0 | { return m_base.getPathSegmentCount(); } |
141 | | |
142 | | virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override |
143 | 0 | { return m_base.getPathSegment(index); } |
144 | | |
145 | | virtual sal_Bool SAL_CALL hasQuery() override |
146 | 27.5k | { return m_base.hasQuery(); } |
147 | | |
148 | | virtual OUString SAL_CALL getQuery() override |
149 | 0 | { return m_base.getQuery(); } |
150 | | |
151 | | virtual sal_Bool SAL_CALL hasFragment() override |
152 | 27.5k | { return m_base.hasFragment(); } |
153 | | |
154 | | virtual OUString SAL_CALL getFragment() override |
155 | 0 | { return m_base.getFragment(); } |
156 | | |
157 | | virtual void SAL_CALL setFragment(OUString const & fragment) override |
158 | 14 | { m_base.setFragment(fragment); } |
159 | | |
160 | | virtual void SAL_CALL clearFragment() override |
161 | 34.6k | { m_base.clearFragment(); } |
162 | | |
163 | | private: |
164 | 152k | virtual ~UriReference() override {} |
165 | | |
166 | | stoc::uriproc::UriReference m_base; |
167 | | }; |
168 | | |
169 | | css::uno::Reference< css::uri::XUriReference > parseGeneric( |
170 | | OUString const & scheme, std::u16string_view schemeSpecificPart) |
171 | 152k | { |
172 | 152k | size_t len = schemeSpecificPart.size(); |
173 | 152k | size_t i = 0; |
174 | 152k | bool hasAuthority = false; |
175 | 152k | OUString authority; |
176 | 152k | if (len - i >= 2 && schemeSpecificPart[i] == '/' |
177 | 152k | && schemeSpecificPart[i + 1] == '/') |
178 | 96.4k | { |
179 | 96.4k | i += 2; |
180 | 96.4k | sal_Int32 n = i; |
181 | 162k | while (i < len && schemeSpecificPart[i] != '/' |
182 | 162k | && schemeSpecificPart[i] != '?') { |
183 | 65.9k | ++i; |
184 | 65.9k | } |
185 | 96.4k | hasAuthority = true; |
186 | 96.4k | authority = schemeSpecificPart.substr(n, i - n); |
187 | 96.4k | } |
188 | 152k | sal_Int32 n = i; |
189 | 152k | i = schemeSpecificPart.find('?', i); |
190 | 152k | if (i == std::u16string_view::npos) { |
191 | 150k | i = len; |
192 | 150k | } |
193 | 152k | OUString path( schemeSpecificPart.substr(n, i - n) ); |
194 | 152k | bool hasQuery = false; |
195 | 152k | OUString query; |
196 | 152k | if (i != len) { |
197 | 1.77k | hasQuery = true; |
198 | 1.77k | query = schemeSpecificPart.substr(i + 1); |
199 | 1.77k | } |
200 | 152k | return new UriReference( |
201 | 152k | scheme, hasAuthority, authority, path, hasQuery, query); |
202 | 152k | } |
203 | | |
204 | | struct Segment { |
205 | | bool leadingSlash; |
206 | | bool excessParent; |
207 | | std::u16string_view segment; |
208 | | |
209 | | Segment(bool theLeadingSlash, bool theExcessParent, std::u16string_view theSegment): |
210 | 60.0k | leadingSlash(theLeadingSlash), excessParent(theExcessParent), segment(theSegment) {} |
211 | | }; |
212 | | |
213 | | std::pair<std::vector<Segment>, bool> processSegments( |
214 | | std::u16string_view first, std::u16string_view second, bool processSpecialSegments) |
215 | 27.5k | { |
216 | 27.5k | std::vector<Segment> segments; |
217 | 27.5k | bool processed = false; |
218 | 27.5k | std::u16string_view const * half = &first; |
219 | | // later checks for `half == &first` and `half == &second` rely on the fact that `first` and |
220 | | // `second` are passed by value, in case a caller passes the same object for both arguments |
221 | 27.5k | std::size_t index = 0; |
222 | 27.5k | bool slash = false; |
223 | 27.5k | if (index == half->length()) { |
224 | 0 | half = &second; |
225 | 0 | index = 0; |
226 | 0 | } |
227 | 27.5k | if (index != half->length()) { |
228 | 27.5k | if ((*half)[index] == u'/') { |
229 | 27.5k | slash = true; |
230 | 27.5k | ++index; |
231 | 27.5k | } |
232 | 89.0k | for (;;) { |
233 | 89.0k | if (index == half->length() && half == &first) { |
234 | 27.4k | half = &second; |
235 | 27.4k | index = 0; |
236 | 27.4k | } |
237 | 89.0k | if (index == half->length()) { |
238 | 27.5k | if (slash) { |
239 | 0 | segments.emplace_back(true, false, std::u16string_view()); |
240 | 0 | } |
241 | 27.5k | break; |
242 | 27.5k | } |
243 | 61.5k | auto const n = std::min(half->find(u'/', index), half->length()); |
244 | 61.5k | auto const leadingSlash = slash; |
245 | 61.5k | auto const segment = half->substr(index, n - index); |
246 | 61.5k | auto const process = processSpecialSegments || half == &second; |
247 | 61.5k | index = n; |
248 | 61.5k | slash = false; |
249 | 61.5k | if (index == half->length() && half == &first) { |
250 | 39 | half = &second; |
251 | 39 | index = 0; |
252 | 39 | } |
253 | 61.5k | if (index != half->length() && (*half)[index] == u'/') { |
254 | 34.0k | slash = true; |
255 | 34.0k | ++index; |
256 | 34.0k | } |
257 | 61.5k | if (process) { |
258 | 61.5k | if (segment == u".") { |
259 | 0 | slash = leadingSlash; |
260 | 0 | processed = true; |
261 | 0 | continue; |
262 | 61.5k | } else if (segment == u"..") { |
263 | 1.43k | if (segments.empty() || segments.back().excessParent) { |
264 | 0 | segments.emplace_back(leadingSlash, true, segment); |
265 | 1.43k | } else { |
266 | 1.43k | if (leadingSlash) { |
267 | 1.43k | segments.pop_back(); |
268 | 1.43k | } |
269 | 1.43k | slash = leadingSlash; |
270 | 1.43k | } |
271 | 1.43k | processed = true; |
272 | 1.43k | continue; |
273 | 1.43k | } |
274 | 61.5k | } |
275 | 60.0k | segments.emplace_back(leadingSlash, false, segment); |
276 | 60.0k | } |
277 | 27.5k | } |
278 | 27.5k | return {segments, processed}; |
279 | 27.5k | } |
280 | | |
281 | | class Factory: |
282 | | public cppu::WeakImplHelper< |
283 | | css::lang::XServiceInfo, css::uri::XUriReferenceFactory> |
284 | | { |
285 | | public: |
286 | | explicit Factory( |
287 | | css::uno::Reference< css::uno::XComponentContext > context): |
288 | 34.7k | m_context(std::move(context)) {} |
289 | | |
290 | | Factory(const Factory&) = delete; |
291 | | Factory& operator=(const Factory&) = delete; |
292 | | |
293 | | virtual OUString SAL_CALL getImplementationName() override; |
294 | | |
295 | | virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override; |
296 | | |
297 | | virtual css::uno::Sequence< OUString > SAL_CALL |
298 | | getSupportedServiceNames() override; |
299 | | |
300 | | virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL |
301 | | parse(OUString const & uriReference) override; |
302 | | |
303 | | virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL |
304 | | makeAbsolute( |
305 | | css::uno::Reference< css::uri::XUriReference > const & baseUriReference, |
306 | | css::uno::Reference< css::uri::XUriReference > const & uriReference, |
307 | | sal_Bool processAdditionalSpecialSegments, |
308 | | css::uri::RelativeUriExcessParentSegments excessParentSegments) override; |
309 | | |
310 | | virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL |
311 | | makeRelative( |
312 | | css::uno::Reference< css::uri::XUriReference > const & baseUriReference, |
313 | | css::uno::Reference< css::uri::XUriReference > const & uriReference, |
314 | | sal_Bool preferAuthorityOverRelativePath, |
315 | | sal_Bool preferAbsoluteOverRelativePath, |
316 | | sal_Bool encodeRetainedSpecialSegments) override; |
317 | | |
318 | | private: |
319 | 34.7k | virtual ~Factory() override {} |
320 | | |
321 | | css::uno::Reference< css::uri::XUriReference > clone( |
322 | | css::uno::Reference< css::uri::XUriReference > const & uriReference) |
323 | 0 | { return parse(uriReference->getUriReference()); } |
324 | | |
325 | | css::uno::Reference< css::uno::XComponentContext > m_context; |
326 | | }; |
327 | | |
328 | | OUString Factory::getImplementationName() |
329 | 0 | { |
330 | 0 | return u"com.sun.star.comp.uri.UriReferenceFactory"_ustr; |
331 | 0 | } |
332 | | |
333 | | sal_Bool Factory::supportsService(OUString const & serviceName) |
334 | 0 | { |
335 | 0 | return cppu::supportsService(this, serviceName); |
336 | 0 | } |
337 | | |
338 | | css::uno::Sequence< OUString > Factory::getSupportedServiceNames() |
339 | 0 | { |
340 | 0 | css::uno::Sequence< OUString > s { u"com.sun.star.uri.UriReferenceFactory"_ustr }; |
341 | 0 | return s; |
342 | 0 | } |
343 | | |
344 | | css::uno::Reference< css::uri::XUriReference > Factory::parse( |
345 | | OUString const & uriReference) |
346 | 152k | { |
347 | 152k | sal_Int32 fragment = uriReference.indexOf('#'); |
348 | 152k | if (fragment == -1) { |
349 | 152k | fragment = uriReference.getLength(); |
350 | 152k | } |
351 | 152k | OUString scheme; |
352 | 152k | OUString schemeSpecificPart; |
353 | 152k | OUString serviceName; |
354 | 152k | sal_Int32 n = parseScheme(uriReference); |
355 | 152k | assert(n < fragment); |
356 | 152k | if (n >= 0) { |
357 | 105k | scheme = uriReference.copy(0, n); |
358 | 105k | schemeSpecificPart = uriReference.copy(n + 1, fragment - (n + 1)); |
359 | 105k | OUStringBuffer buf(128); |
360 | 105k | buf.append("com.sun.star.uri.UriSchemeParser_"); |
361 | 648k | for (sal_Int32 i = 0; i < scheme.getLength(); ++i) { |
362 | 542k | sal_Unicode c = scheme[i]; |
363 | 542k | if (rtl::isAsciiUpperCase(c)) { |
364 | 2 | buf.append(static_cast<sal_Unicode>(rtl::toAsciiLowerCase(c))); |
365 | 542k | } else if (c == '+') { |
366 | 47 | buf.append("PLUS"); |
367 | 542k | } else if (c == '-') { |
368 | 8 | buf.append("HYPHEN"); |
369 | 542k | } else if (c == '.') { |
370 | 27.2k | buf.append("DOT"); |
371 | 515k | } else { |
372 | 515k | assert(rtl::isAsciiLowerCase(c) || rtl::isAsciiDigit(c)); |
373 | 515k | buf.append(c); |
374 | 515k | } |
375 | 542k | } |
376 | 105k | serviceName = buf.makeStringAndClear(); |
377 | 105k | } else { |
378 | 46.3k | schemeSpecificPart = uriReference.copy(0, fragment); |
379 | 46.3k | } |
380 | 152k | css::uno::Reference< css::uri::XUriSchemeParser > parser; |
381 | 152k | if (!serviceName.isEmpty()) { |
382 | 105k | css::uno::Reference< css::lang::XMultiComponentFactory > factory( |
383 | 105k | m_context->getServiceManager()); |
384 | 105k | if (factory.is()) { |
385 | 105k | css::uno::Reference< css::uno::XInterface > service; |
386 | 105k | try { |
387 | 105k | service = factory->createInstanceWithContext( |
388 | 105k | serviceName, m_context); |
389 | 105k | } catch (css::uno::RuntimeException &) { |
390 | 0 | throw; |
391 | 0 | } catch (const css::uno::Exception &) { |
392 | 0 | css::uno::Any anyEx = cppu::getCaughtException(); |
393 | 0 | throw css::lang::WrappedTargetRuntimeException( |
394 | 0 | "creating service " + serviceName, |
395 | 0 | getXWeak(), |
396 | 0 | anyEx); |
397 | 0 | } |
398 | 105k | if (service.is()) { |
399 | 0 | parser.set( service, css::uno::UNO_QUERY_THROW); |
400 | 0 | } |
401 | 105k | } |
402 | 105k | } |
403 | 152k | css::uno::Reference< css::uri::XUriReference > uriRef( |
404 | 152k | parser.is() |
405 | 152k | ? parser->parse(scheme, schemeSpecificPart) |
406 | 152k | : parseGeneric(scheme, schemeSpecificPart)); |
407 | 152k | if (uriRef.is() && fragment != uriReference.getLength()) { |
408 | 14 | uriRef->setFragment(uriReference.copy(fragment + 1)); |
409 | 14 | } |
410 | 152k | return uriRef; |
411 | 152k | } |
412 | | |
413 | | css::uno::Reference< css::uri::XUriReference > Factory::makeAbsolute( |
414 | | css::uno::Reference< css::uri::XUriReference > const & baseUriReference, |
415 | | css::uno::Reference< css::uri::XUriReference > const & uriReference, |
416 | | sal_Bool processAdditionalSpecialSegments, |
417 | | css::uri::RelativeUriExcessParentSegments excessParentSegments) |
418 | 27.5k | { |
419 | 27.5k | if (!baseUriReference.is() || !baseUriReference->isAbsolute() |
420 | 27.5k | || !uriReference.is()) { |
421 | 0 | return nullptr; |
422 | 27.5k | } else if (uriReference->isAbsolute()) { |
423 | 0 | if (processAdditionalSpecialSegments) { |
424 | 0 | auto const path = uriReference->getPath(); |
425 | 0 | auto [segments, proc] = processSegments(path, {}, true); |
426 | 0 | if (proc) { |
427 | 0 | OUStringBuffer abs(uriReference->getScheme() + ":"); |
428 | 0 | if (uriReference->hasAuthority()) { |
429 | 0 | abs.append("//" + uriReference->getAuthority()); |
430 | 0 | } |
431 | 0 | for (auto const & i : segments) |
432 | 0 | { |
433 | 0 | if (i.excessParent) { |
434 | 0 | switch (excessParentSegments) { |
435 | 0 | case css::uri::RelativeUriExcessParentSegments_ERROR: |
436 | 0 | return nullptr; |
437 | | |
438 | 0 | case css::uri::RelativeUriExcessParentSegments_RETAIN: |
439 | 0 | assert(i.segment == u".."); |
440 | 0 | break; |
441 | | |
442 | 0 | case css::uri::RelativeUriExcessParentSegments_REMOVE: |
443 | 0 | continue; |
444 | | |
445 | 0 | default: |
446 | 0 | assert(false); |
447 | 0 | break; |
448 | 0 | } |
449 | 0 | } |
450 | 0 | if (i.leadingSlash) { |
451 | 0 | abs.append('/'); |
452 | 0 | } |
453 | 0 | abs.append(i.segment); |
454 | 0 | } |
455 | 0 | if (uriReference->hasQuery()) { |
456 | 0 | abs.append("?" + uriReference->getQuery()); |
457 | 0 | } |
458 | 0 | if (uriReference->hasFragment()) { |
459 | 0 | abs.append("#" + uriReference->getFragment()); |
460 | 0 | } |
461 | 0 | return parse(abs.makeStringAndClear()); |
462 | 0 | } |
463 | 0 | } |
464 | 0 | return clone(uriReference); |
465 | 27.5k | } else if (!uriReference->hasAuthority() |
466 | 27.5k | && uriReference->getPath().isEmpty()) { |
467 | 0 | OUStringBuffer abs(baseUriReference->getScheme() + ":"); |
468 | 0 | if (baseUriReference->hasAuthority()) { |
469 | 0 | abs.append("//" + baseUriReference->getAuthority()); |
470 | 0 | } |
471 | 0 | abs.append(baseUriReference->getPath()); |
472 | 0 | if (uriReference->hasQuery()) { |
473 | 0 | abs.append("?" + uriReference->getQuery()); |
474 | 0 | } else if (baseUriReference->hasQuery()) { |
475 | 0 | abs.append("?" + baseUriReference->getQuery()); |
476 | 0 | } |
477 | 0 | if (uriReference->hasFragment()) { |
478 | 0 | abs.append("#" + uriReference->getFragment()); |
479 | 0 | } |
480 | 0 | return parse(abs.makeStringAndClear()); |
481 | 27.5k | } else { |
482 | 27.5k | OUStringBuffer abs(128); |
483 | 27.5k | abs.append(baseUriReference->getScheme() + ":"); |
484 | 27.5k | if (uriReference->hasAuthority()) { |
485 | 0 | abs.append("//" + uriReference->getAuthority()); |
486 | 27.5k | } else if (baseUriReference->hasAuthority()) { |
487 | 27.5k | abs.append("//" + baseUriReference->getAuthority()); |
488 | 27.5k | } |
489 | 27.5k | if (uriReference->hasRelativePath()) { |
490 | 27.4k | auto path1 = baseUriReference->getPath(); |
491 | 27.4k | if (path1.isEmpty()) { |
492 | 0 | if (baseUriReference->hasAuthority()) { |
493 | 0 | path1 = "/"; |
494 | 0 | } |
495 | 27.4k | } else { |
496 | 27.4k | path1 = path1.copy(0, path1.lastIndexOf('/') + 1); |
497 | 27.4k | } |
498 | 27.4k | auto const path2 = uriReference->getPath(); |
499 | 27.4k | auto [segments, _] = processSegments(path1, path2, processAdditionalSpecialSegments); |
500 | 27.4k | (void)_; |
501 | 27.4k | for (auto const & i : segments) |
502 | 58.5k | { |
503 | 58.5k | if (i.excessParent) { |
504 | 0 | switch (excessParentSegments) { |
505 | 0 | case css::uri::RelativeUriExcessParentSegments_ERROR: |
506 | 0 | return nullptr; |
507 | | |
508 | 0 | case css::uri::RelativeUriExcessParentSegments_RETAIN: |
509 | 0 | assert(i.segment == u".."); |
510 | 0 | break; |
511 | | |
512 | 0 | case css::uri::RelativeUriExcessParentSegments_REMOVE: |
513 | 0 | continue; |
514 | | |
515 | 0 | default: |
516 | 0 | assert(false); |
517 | 0 | break; |
518 | 0 | } |
519 | 0 | } |
520 | 58.5k | if (i.leadingSlash) { |
521 | 58.5k | abs.append('/'); |
522 | 58.5k | } |
523 | 58.5k | abs.append(i.segment); |
524 | 58.5k | } |
525 | 27.4k | } else { |
526 | 39 | bool processed = false; |
527 | 39 | if (processAdditionalSpecialSegments) { |
528 | 39 | auto const path = uriReference->getPath(); |
529 | 39 | auto [segments, proc] = processSegments(path, {}, true); |
530 | 39 | if (proc) { |
531 | 0 | for (auto const & i : segments) |
532 | 0 | { |
533 | 0 | if (i.excessParent) { |
534 | 0 | switch (excessParentSegments) { |
535 | 0 | case css::uri::RelativeUriExcessParentSegments_ERROR: |
536 | 0 | return nullptr; |
537 | | |
538 | 0 | case css::uri::RelativeUriExcessParentSegments_RETAIN: |
539 | 0 | assert(i.segment == u".."); |
540 | 0 | break; |
541 | | |
542 | 0 | case css::uri::RelativeUriExcessParentSegments_REMOVE: |
543 | 0 | continue; |
544 | | |
545 | 0 | default: |
546 | 0 | assert(false); |
547 | 0 | break; |
548 | 0 | } |
549 | 0 | } |
550 | 0 | if (i.leadingSlash) { |
551 | 0 | abs.append('/'); |
552 | 0 | } |
553 | 0 | abs.append(i.segment); |
554 | 0 | } |
555 | 0 | processed = true; |
556 | 0 | } |
557 | 39 | } |
558 | 39 | if (!processed) { |
559 | 39 | abs.append(uriReference->getPath()); |
560 | 39 | } |
561 | 39 | } |
562 | 27.5k | if (uriReference->hasQuery()) { |
563 | 0 | abs.append("?" + uriReference->getQuery()); |
564 | 0 | } |
565 | 27.5k | if (uriReference->hasFragment()) { |
566 | 0 | abs.append("#" + uriReference->getFragment()); |
567 | 0 | } |
568 | 27.5k | return parse(abs.makeStringAndClear()); |
569 | 27.5k | } |
570 | 27.5k | } |
571 | | |
572 | | css::uno::Reference< css::uri::XUriReference > Factory::makeRelative( |
573 | | css::uno::Reference< css::uri::XUriReference > const & baseUriReference, |
574 | | css::uno::Reference< css::uri::XUriReference > const & uriReference, |
575 | | sal_Bool preferAuthorityOverRelativePath, |
576 | | sal_Bool preferAbsoluteOverRelativePath, |
577 | | sal_Bool encodeRetainedSpecialSegments) |
578 | 0 | { |
579 | 0 | if (!baseUriReference.is() || !baseUriReference->isAbsolute() |
580 | 0 | || !uriReference.is()) { |
581 | 0 | return nullptr; |
582 | 0 | } else if (!uriReference->isAbsolute() || uriReference->hasRelativePath() |
583 | 0 | || !baseUriReference->getScheme().equalsIgnoreAsciiCase( |
584 | 0 | uriReference->getScheme())) { |
585 | 0 | return clone(uriReference); |
586 | 0 | } else { |
587 | 0 | OUStringBuffer rel(128); |
588 | 0 | bool omitQuery = false; |
589 | 0 | if ((baseUriReference->hasAuthority() != uriReference->hasAuthority()) |
590 | 0 | || !equalIgnoreEscapeCase( |
591 | 0 | baseUriReference->getAuthority(), |
592 | 0 | uriReference->getAuthority())) |
593 | 0 | { |
594 | 0 | if (uriReference->hasAuthority()) { |
595 | 0 | rel.append("//" + uriReference->getAuthority()); |
596 | 0 | } |
597 | 0 | rel.append(uriReference->getPath()); |
598 | 0 | } else if ((equalIgnoreEscapeCase( |
599 | 0 | baseUriReference->getPath(), uriReference->getPath()) |
600 | 0 | || (baseUriReference->getPath() == "/" |
601 | 0 | && uriReference->getPath().isEmpty())) |
602 | 0 | && baseUriReference->hasQuery() == uriReference->hasQuery() |
603 | 0 | && equalIgnoreEscapeCase( |
604 | 0 | baseUriReference->getQuery(), uriReference->getQuery())) |
605 | 0 | { |
606 | 0 | omitQuery = true; |
607 | 0 | } else { |
608 | 0 | sal_Int32 count1 = std::max< sal_Int32 >( |
609 | 0 | baseUriReference->getPathSegmentCount(), 1); |
610 | 0 | sal_Int32 count2 = std::max< sal_Int32 >( |
611 | 0 | uriReference->getPathSegmentCount(), 1); |
612 | 0 | sal_Int32 i = 0; |
613 | 0 | for (; i < std::min(count1, count2) - 1; ++i) { |
614 | 0 | if (!equalIgnoreEscapeCase( |
615 | 0 | baseUriReference->getPathSegment(i), |
616 | 0 | uriReference->getPathSegment(i))) |
617 | 0 | { |
618 | 0 | break; |
619 | 0 | } |
620 | 0 | } |
621 | 0 | if (i == 0 |
622 | 0 | && (preferAbsoluteOverRelativePath || uriReference->hasQuery()) |
623 | 0 | && (preferAuthorityOverRelativePath |
624 | 0 | || !uriReference->getPath().startsWith("//"))) |
625 | 0 | { |
626 | 0 | if (uriReference->getPath().isEmpty()) { |
627 | 0 | if (!baseUriReference->getPath().isEmpty() |
628 | 0 | && baseUriReference->getPath() != "/") |
629 | 0 | { |
630 | 0 | rel.append('/'); |
631 | 0 | } |
632 | 0 | } else if (uriReference->getPath() == "/") { |
633 | 0 | if (baseUriReference->getPath().isEmpty() |
634 | 0 | || baseUriReference->getPath() != "/") |
635 | 0 | { |
636 | 0 | rel.append('/'); |
637 | 0 | } |
638 | 0 | } else { |
639 | 0 | if (uriReference->getPath().startsWith("//")) { |
640 | 0 | assert(uriReference->hasAuthority()); |
641 | 0 | rel.append("//" + uriReference->getAuthority()); |
642 | 0 | } |
643 | 0 | rel.append(uriReference->getPath()); |
644 | 0 | } |
645 | 0 | } else { |
646 | 0 | bool segments = false; |
647 | 0 | for (sal_Int32 j = i; j < count1 - 1; ++j) { |
648 | 0 | if (segments) { |
649 | 0 | rel.append('/'); |
650 | 0 | } |
651 | 0 | rel.append(".."); |
652 | 0 | segments = true; |
653 | 0 | } |
654 | 0 | if (i < count2 - 1 |
655 | 0 | || (!uriReference->getPathSegment(count2 - 1).isEmpty())) |
656 | 0 | { |
657 | 0 | if (!segments |
658 | 0 | && (uriReference->getPathSegment(i).isEmpty() |
659 | 0 | || (parseScheme(uriReference->getPathSegment(i)) |
660 | 0 | >= 0))) |
661 | 0 | { |
662 | 0 | rel.append('.'); |
663 | 0 | segments = true; |
664 | 0 | } |
665 | 0 | for (; i < count2; ++i) { |
666 | 0 | if (segments) { |
667 | 0 | rel.append('/'); |
668 | 0 | } |
669 | 0 | OUString s(uriReference->getPathSegment(i)); |
670 | 0 | if (encodeRetainedSpecialSegments && s == ".") { |
671 | 0 | rel.append("%2E"); |
672 | 0 | } else if (encodeRetainedSpecialSegments && s == "..") { |
673 | 0 | rel.append("%2E%2E"); |
674 | 0 | } else { |
675 | 0 | rel.append(s); |
676 | 0 | } |
677 | 0 | segments = true; |
678 | 0 | } |
679 | 0 | } |
680 | 0 | } |
681 | 0 | } |
682 | 0 | if (!omitQuery && uriReference->hasQuery()) { |
683 | 0 | rel.append("?" + uriReference->getQuery()); |
684 | 0 | } |
685 | 0 | if (uriReference->hasFragment()) { |
686 | 0 | rel.append("#" + uriReference->getFragment()); |
687 | 0 | } |
688 | 0 | return parse(rel.makeStringAndClear()); |
689 | 0 | } |
690 | 0 | } |
691 | | |
692 | | } |
693 | | |
694 | | extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* |
695 | | com_sun_star_comp_uri_UriReferenceFactory_get_implementation(css::uno::XComponentContext* rxContext, |
696 | | css::uno::Sequence<css::uno::Any> const &) |
697 | 34.7k | { |
698 | 34.7k | return ::cppu::acquire(new Factory(rxContext)); |
699 | 34.7k | } |
700 | | |
701 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |