Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / stoc / source / uriproc / UriReferenceFactory.cxx
blob71efcf27594553ed1d0430f089daeaed04ff9dd1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <algorithm>
23 #include <cassert>
24 #include <cstdlib>
25 #include <exception>
26 #include <vector>
28 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
29 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
30 #include <com/sun/star/lang/XServiceInfo.hpp>
31 #include <com/sun/star/uno/Any.hxx>
32 #include <com/sun/star/uno/Exception.hpp>
33 #include <com/sun/star/uno/Reference.hxx>
34 #include <com/sun/star/uno/RuntimeException.hpp>
35 #include <com/sun/star/uno/Sequence.hxx>
36 #include <com/sun/star/uno/XComponentContext.hpp>
37 #include <com/sun/star/uno/XInterface.hpp>
38 #include <com/sun/star/uri/RelativeUriExcessParentSegments.hpp>
39 #include <com/sun/star/uri/XUriReference.hpp>
40 #include <com/sun/star/uri/XUriReferenceFactory.hpp>
41 #include <com/sun/star/uri/XUriSchemeParser.hpp>
42 #include <cppuhelper/implbase.hxx>
43 #include <cppuhelper/supportsservice.hxx>
44 #include <cppuhelper/weak.hxx>
45 #include <rtl/character.hxx>
46 #include <rtl/ustrbuf.hxx>
47 #include <rtl/ustring.hxx>
48 #include <sal/types.h>
50 #include "UriReference.hxx"
52 namespace {
54 bool equalIgnoreEscapeCase(OUString const & s1, OUString const & s2) {
55 if (s1.getLength() == s2.getLength()) {
56 for (sal_Int32 i = 0; i < s1.getLength();) {
57 if (s1[i] == '%' && s2[i] == '%' && s1.getLength() - i > 2
58 && rtl::isAsciiHexDigit(s1[i + 1])
59 && rtl::isAsciiHexDigit(s1[i + 2])
60 && rtl::isAsciiHexDigit(s2[i + 1])
61 && rtl::isAsciiHexDigit(s2[i + 2])
62 && rtl::compareIgnoreAsciiCase(s1[i + 1], s2[i + 1]) == 0
63 && rtl::compareIgnoreAsciiCase(s1[i + 2], s2[i + 2]) == 0)
65 i += 3;
66 } else if (s1[i] != s2[i]) {
67 return false;
68 } else {
69 ++i;
72 return true;
73 } else {
74 return false;
78 sal_Int32 parseScheme(OUString const & uriReference) {
79 if (uriReference.getLength() >= 2 && rtl::isAsciiAlpha(uriReference[0])) {
80 for (sal_Int32 i = 0; i < uriReference.getLength(); ++i) {
81 sal_Unicode c = uriReference[i];
82 if (c == ':') {
83 return i;
84 } else if (!rtl::isAsciiAlpha(c) && !rtl::isAsciiDigit(c)
85 && c != '+' && c != '-' && c != '.')
87 break;
91 return -1;
94 class UriReference:
95 public cppu::WeakImplHelper<css::uri::XUriReference>
97 public:
98 UriReference(
99 OUString const & scheme, bool bIsHierarchical, bool bHasAuthority,
100 OUString const & authority, OUString const & path,
101 bool bHasQuery, OUString const & query):
102 m_base(
103 scheme, bIsHierarchical, bHasAuthority, authority, path, bHasQuery,
104 query)
107 UriReference(const UriReference&) = delete;
108 UriReference& operator=(const UriReference&) = delete;
110 virtual OUString SAL_CALL getUriReference() override
111 { return m_base.getUriReference(); }
113 virtual sal_Bool SAL_CALL isAbsolute() override
114 { return m_base.isAbsolute(); }
116 virtual OUString SAL_CALL getScheme() override
117 { return m_base.getScheme(); }
119 virtual OUString SAL_CALL getSchemeSpecificPart() override
120 { return m_base.getSchemeSpecificPart(); }
122 virtual sal_Bool SAL_CALL isHierarchical() override
123 { return m_base.isHierarchical(); }
125 virtual sal_Bool SAL_CALL hasAuthority() override
126 { return m_base.hasAuthority(); }
128 virtual OUString SAL_CALL getAuthority() override
129 { return m_base.getAuthority(); }
131 virtual OUString SAL_CALL getPath() override
132 { return m_base.getPath(); }
134 virtual sal_Bool SAL_CALL hasRelativePath() override
135 { return m_base.hasRelativePath(); }
137 virtual sal_Int32 SAL_CALL getPathSegmentCount() override
138 { return m_base.getPathSegmentCount(); }
140 virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override
141 { return m_base.getPathSegment(index); }
143 virtual sal_Bool SAL_CALL hasQuery() override
144 { return m_base.hasQuery(); }
146 virtual OUString SAL_CALL getQuery() override
147 { return m_base.getQuery(); }
149 virtual sal_Bool SAL_CALL hasFragment() override
150 { return m_base.hasFragment(); }
152 virtual OUString SAL_CALL getFragment() override
153 { return m_base.getFragment(); }
155 virtual void SAL_CALL setFragment(OUString const & fragment) override
156 { m_base.setFragment(fragment); }
158 virtual void SAL_CALL clearFragment() override
159 { m_base.clearFragment(); }
161 private:
162 virtual ~UriReference() override {}
164 stoc::uriproc::UriReference m_base;
167 css::uno::Reference< css::uri::XUriReference > parseGeneric(
168 OUString const & scheme, OUString const & schemeSpecificPart)
170 bool isAbsolute = !scheme.isEmpty();
171 bool isHierarchical = !isAbsolute || schemeSpecificPart.startsWith("/");
172 bool hasAuthority = false;
173 OUString authority;
174 OUString path;
175 bool hasQuery = false;
176 OUString query;
177 if (isHierarchical) {
178 sal_Int32 len = schemeSpecificPart.getLength();
179 sal_Int32 i = 0;
180 if (len - i >= 2 && schemeSpecificPart[i] == '/'
181 && schemeSpecificPart[i + 1] == '/')
183 i += 2;
184 sal_Int32 n = i;
185 while (i < len && schemeSpecificPart[i] != '/'
186 && schemeSpecificPart[i] != '?') {
187 ++i;
189 hasAuthority = true;
190 authority = schemeSpecificPart.copy(n, i - n);
192 sal_Int32 n = i;
193 i = schemeSpecificPart.indexOf('?', i);
194 if (i == -1) {
195 i = len;
197 path = schemeSpecificPart.copy(n, i - n);
198 if (i != len) {
199 hasQuery = true;
200 query = schemeSpecificPart.copy(i + 1);
202 } else {
203 if (schemeSpecificPart.isEmpty()) {
204 // The scheme-specific part of an opaque URI must not be empty:
205 return nullptr;
207 path = schemeSpecificPart;
209 return new UriReference(
210 scheme, isHierarchical, hasAuthority, authority, path, hasQuery, query);
213 typedef std::vector< sal_Int32 > Segments;
215 void processSegments(
216 Segments & segments,
217 css::uno::Reference< css::uri::XUriReference > const & uriReference,
218 bool base, bool processSpecialSegments)
220 sal_Int32 count = uriReference->getPathSegmentCount() - (base ? 1 : 0);
221 assert(count <= SAL_MAX_INT32 - 1 && -count >= SAL_MIN_INT32 + 1);
222 for (sal_Int32 i = 0; i < count; ++i) {
223 if (processSpecialSegments) {
224 OUString segment(uriReference->getPathSegment(i));
225 if ( segment == "." ) {
226 if (!base && i == count - 1) {
227 segments.push_back(0);
229 continue;
230 } else if ( segment == ".." ) {
231 if (segments.empty() || std::abs(segments.back()) == 1) {
232 segments.push_back(base ? -1 : 1);
233 } else {
234 segments.pop_back();
236 continue;
239 segments.push_back(base ? -(i + 2) : i + 2);
243 class Factory:
244 public cppu::WeakImplHelper<
245 css::lang::XServiceInfo, css::uri::XUriReferenceFactory>
247 public:
248 explicit Factory(
249 css::uno::Reference< css::uno::XComponentContext > const & context):
250 m_context(context) {}
252 Factory(const Factory&) = delete;
253 Factory& operator=(const Factory&) = delete;
255 virtual OUString SAL_CALL getImplementationName() override;
257 virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override;
259 virtual css::uno::Sequence< OUString > SAL_CALL
260 getSupportedServiceNames() override;
262 virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
263 parse(OUString const & uriReference) override;
265 virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
266 makeAbsolute(
267 css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
268 css::uno::Reference< css::uri::XUriReference > const & uriReference,
269 sal_Bool processSpecialBaseSegments,
270 css::uri::RelativeUriExcessParentSegments excessParentSegments) override;
272 virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
273 makeRelative(
274 css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
275 css::uno::Reference< css::uri::XUriReference > const & uriReference,
276 sal_Bool preferAuthorityOverRelativePath,
277 sal_Bool preferAbsoluteOverRelativePath,
278 sal_Bool encodeRetainedSpecialSegments) override;
280 private:
281 virtual ~Factory() override {}
283 css::uno::Reference< css::uri::XUriReference > clone(
284 css::uno::Reference< css::uri::XUriReference > const & uriReference)
285 { return parse(uriReference->getUriReference()); }
287 css::uno::Reference< css::uno::XComponentContext > m_context;
290 OUString Factory::getImplementationName()
292 return OUString("com.sun.star.comp.uri.UriReferenceFactory");
295 sal_Bool Factory::supportsService(OUString const & serviceName)
297 return cppu::supportsService(this, serviceName);
300 css::uno::Sequence< OUString > Factory::getSupportedServiceNames()
302 css::uno::Sequence< OUString > s { "com.sun.star.uri.UriReferenceFactory" };
303 return s;
306 css::uno::Reference< css::uri::XUriReference > Factory::parse(
307 OUString const & uriReference)
309 sal_Int32 fragment = uriReference.indexOf('#');
310 if (fragment == -1) {
311 fragment = uriReference.getLength();
313 OUString scheme;
314 OUString schemeSpecificPart;
315 OUString serviceName;
316 sal_Int32 n = parseScheme(uriReference);
317 assert(n < fragment);
318 if (n >= 0) {
319 scheme = uriReference.copy(0, n);
320 schemeSpecificPart = uriReference.copy(n + 1, fragment - (n + 1));
321 OUStringBuffer buf;
322 buf.append("com.sun.star.uri.UriSchemeParser_");
323 for (sal_Int32 i = 0; i < scheme.getLength(); ++i) {
324 sal_Unicode c = scheme[i];
325 if (rtl::isAsciiUpperCase(c)) {
326 buf.append(static_cast<sal_Unicode>(rtl::toAsciiLowerCase(c)));
327 } else if (c == '+') {
328 buf.append("PLUS");
329 } else if (c == '-') {
330 buf.append("HYPHEN");
331 } else if (c == '.') {
332 buf.append("DOT");
333 } else {
334 assert(rtl::isAsciiLowerCase(c) || rtl::isAsciiDigit(c));
335 buf.append(c);
338 serviceName = buf.makeStringAndClear();
339 } else {
340 schemeSpecificPart = uriReference.copy(0, fragment);
342 css::uno::Reference< css::uri::XUriSchemeParser > parser;
343 if (!serviceName.isEmpty()) {
344 css::uno::Reference< css::lang::XMultiComponentFactory > factory(
345 m_context->getServiceManager());
346 if (factory.is()) {
347 css::uno::Reference< css::uno::XInterface > service;
348 try {
349 service = factory->createInstanceWithContext(
350 serviceName, m_context);
351 } catch (css::uno::RuntimeException &) {
352 throw;
353 } catch (const css::uno::Exception & e) {
354 throw css::lang::WrappedTargetRuntimeException(
355 "creating service " + serviceName,
356 static_cast< cppu::OWeakObject * >(this),
357 css::uno::makeAny(e)); //TODO: preserve type of e
359 if (service.is()) {
360 parser.set( service, css::uno::UNO_QUERY_THROW);
364 css::uno::Reference< css::uri::XUriReference > uriRef(
365 parser.is()
366 ? parser->parse(scheme, schemeSpecificPart)
367 : parseGeneric(scheme, schemeSpecificPart));
368 if (uriRef.is() && fragment != uriReference.getLength()) {
369 uriRef->setFragment(uriReference.copy(fragment + 1));
371 return uriRef;
374 css::uno::Reference< css::uri::XUriReference > Factory::makeAbsolute(
375 css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
376 css::uno::Reference< css::uri::XUriReference > const & uriReference,
377 sal_Bool processSpecialBaseSegments,
378 css::uri::RelativeUriExcessParentSegments excessParentSegments)
380 if (!baseUriReference.is() || !baseUriReference->isAbsolute()
381 || !baseUriReference->isHierarchical() || !uriReference.is()) {
382 return nullptr;
383 } else if (uriReference->isAbsolute()) {
384 return clone(uriReference);
385 } else if (!uriReference->hasAuthority()
386 && uriReference->getPath().isEmpty()
387 && !uriReference->hasQuery()) {
388 css::uno::Reference< css::uri::XUriReference > abs(
389 clone(baseUriReference));
390 if (uriReference->hasFragment()) {
391 abs->setFragment(uriReference->getFragment());
392 } else {
393 abs->clearFragment();
395 return abs;
396 } else {
397 OUStringBuffer abs(baseUriReference->getScheme());
398 abs.append(':');
399 if (uriReference->hasAuthority()) {
400 abs.append("//");
401 abs.append(uriReference->getAuthority());
402 } else if (baseUriReference->hasAuthority()) {
403 abs.append("//");
404 abs.append(baseUriReference->getAuthority());
406 if (uriReference->hasRelativePath()) {
407 Segments segments;
408 processSegments(
409 segments, baseUriReference, true, processSpecialBaseSegments);
410 processSegments(segments, uriReference, false, true);
411 // If the path component of the base URI reference is empty (which
412 // implies that the base URI reference denotes a "root entity"), and
413 // the resulting URI reference denotes the same root entity, make
414 // sure the path component of the resulting URI reference is also
415 // empty (and not "/"). RFC 2396 is unclear about this, and I chose
416 // these rules for consistent results.
417 bool slash = !baseUriReference->getPath().isEmpty();
418 if (slash) {
419 abs.append('/');
421 for (Segments::iterator i(segments.begin()); i != segments.end();
422 ++i)
424 if (*i < -1) {
425 OUString segment(
426 baseUriReference->getPathSegment(-(*i + 2)));
427 if (!segment.isEmpty() || segments.size() > 1) {
428 if (!slash) {
429 abs.append('/');
431 abs.append(segment);
432 slash = true;
433 abs.append('/');
435 } else if (*i > 1) {
436 OUString segment(uriReference->getPathSegment(*i - 2));
437 if (!segment.isEmpty() || segments.size() > 1) {
438 if (!slash) {
439 abs.append('/');
441 abs.append(segment);
442 slash = false;
444 } else if (*i == 0) {
445 if (segments.size() > 1 && !slash) {
446 abs.append('/');
448 } else {
449 switch (excessParentSegments) {
450 case css::uri::RelativeUriExcessParentSegments_ERROR:
451 return nullptr;
453 case css::uri::RelativeUriExcessParentSegments_RETAIN:
454 if (!slash) {
455 abs.append('/');
457 abs.append("..");
458 slash = *i < 0;
459 if (slash) {
460 abs.append('/');
462 break;
464 case css::uri::RelativeUriExcessParentSegments_REMOVE:
465 break;
467 default:
468 assert(false);
469 break;
473 } else {
474 abs.append(uriReference->getPath());
476 if (uriReference->hasQuery()) {
477 abs.append('?');
478 abs.append(uriReference->getQuery());
480 if (uriReference->hasFragment()) {
481 abs.append('#');
482 abs.append(uriReference->getFragment());
484 return parse(abs.makeStringAndClear());
488 css::uno::Reference< css::uri::XUriReference > Factory::makeRelative(
489 css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
490 css::uno::Reference< css::uri::XUriReference > const & uriReference,
491 sal_Bool preferAuthorityOverRelativePath,
492 sal_Bool preferAbsoluteOverRelativePath,
493 sal_Bool encodeRetainedSpecialSegments)
495 if (!baseUriReference.is() || !baseUriReference->isAbsolute()
496 || !baseUriReference->isHierarchical() || !uriReference.is()) {
497 return nullptr;
498 } else if (!uriReference->isAbsolute() || !uriReference->isHierarchical()
499 || !baseUriReference->getScheme().equalsIgnoreAsciiCase(
500 uriReference->getScheme())) {
501 return clone(uriReference);
502 } else {
503 OUStringBuffer rel;
504 bool omitQuery = false;
505 if ((baseUriReference->hasAuthority() != uriReference->hasAuthority())
506 || !equalIgnoreEscapeCase(
507 baseUriReference->getAuthority(),
508 uriReference->getAuthority()))
510 if (uriReference->hasAuthority()) {
511 rel.append("//");
512 rel.append(uriReference->getAuthority());
514 rel.append(uriReference->getPath());
515 } else if ((equalIgnoreEscapeCase(
516 baseUriReference->getPath(), uriReference->getPath())
517 || (baseUriReference->getPath().getLength() <= 1
518 && uriReference->getPath().getLength() <= 1))
519 && baseUriReference->hasQuery() == uriReference->hasQuery()
520 && equalIgnoreEscapeCase(
521 baseUriReference->getQuery(), uriReference->getQuery()))
523 omitQuery = true;
524 } else {
525 sal_Int32 count1 = std::max< sal_Int32 >(
526 baseUriReference->getPathSegmentCount(), 1);
527 sal_Int32 count2 = std::max< sal_Int32 >(
528 uriReference->getPathSegmentCount(), 1);
529 sal_Int32 i = 0;
530 for (; i < std::min(count1, count2) - 1; ++i) {
531 if (!equalIgnoreEscapeCase(
532 baseUriReference->getPathSegment(i),
533 uriReference->getPathSegment(i)))
535 break;
538 if (i == 0 && preferAbsoluteOverRelativePath
539 && (preferAuthorityOverRelativePath
540 || !uriReference->getPath().startsWith("//")))
542 if (baseUriReference->getPath().getLength() > 1
543 || uriReference->getPath().getLength() > 1)
545 if (uriReference->getPath().isEmpty()) {
546 rel.append('/');
547 } else {
548 assert(uriReference->getPath()[0] == '/');
549 if (uriReference->getPath().startsWith("//")) {
550 assert(uriReference->hasAuthority());
551 rel.append("//");
552 rel.append(uriReference->getAuthority());
554 rel.append(uriReference->getPath());
557 } else {
558 bool segments = false;
559 for (sal_Int32 j = i; j < count1 - 1; ++j) {
560 if (segments) {
561 rel.append('/');
563 rel.append("..");
564 segments = true;
566 if (i < count2 - 1
567 || (!uriReference->getPathSegment(count2 - 1).isEmpty()))
569 if (!segments
570 && (uriReference->getPathSegment(i).isEmpty()
571 || (parseScheme(uriReference->getPathSegment(i))
572 >= 0)))
574 rel.append('.');
575 segments = true;
577 for (; i < count2; ++i) {
578 if (segments) {
579 rel.append('/');
581 OUString s(uriReference->getPathSegment(i));
582 if (encodeRetainedSpecialSegments && s == ".") {
583 rel.append("%2E");
584 } else if (encodeRetainedSpecialSegments && s == "..") {
585 rel.append("%2E%2E");
586 } else {
587 rel.append(s);
589 segments = true;
594 if (!omitQuery && uriReference->hasQuery()) {
595 rel.append('?');
596 rel.append(uriReference->getQuery());
598 if (uriReference->hasFragment()) {
599 rel.append('#');
600 rel.append(uriReference->getFragment());
602 return parse(rel.makeStringAndClear());
608 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
609 com_sun_star_comp_uri_UriReferenceFactory_get_implementation(css::uno::XComponentContext* rxContext,
610 css::uno::Sequence<css::uno::Any> const &)
612 return ::cppu::acquire(new Factory(rxContext));
615 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */