/src/libreoffice/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTscript.cxx
Line | Count | Source |
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 "UriReference.hxx" |
21 | | |
22 | | #include <com/sun/star/lang/IllegalArgumentException.hpp> |
23 | | #include <com/sun/star/lang/XServiceInfo.hpp> |
24 | | #include <com/sun/star/uno/Reference.hxx> |
25 | | #include <com/sun/star/uno/Sequence.hxx> |
26 | | #include <com/sun/star/uri/XUriSchemeParser.hpp> |
27 | | #include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp> |
28 | | #include <cppuhelper/implbase.hxx> |
29 | | #include <cppuhelper/supportsservice.hxx> |
30 | | #include <cppuhelper/weak.hxx> |
31 | | #include <rtl/character.hxx> |
32 | | #include <rtl/uri.hxx> |
33 | | #include <rtl/ustrbuf.hxx> |
34 | | #include <rtl/ustring.hxx> |
35 | | #include <sal/types.h> |
36 | | #include <o3tl/safeint.hxx> |
37 | | #include <o3tl/numeric.hxx> |
38 | | |
39 | | #include <string_view> |
40 | | |
41 | | namespace com::sun::star::uno { class XComponentContext; } |
42 | | namespace com::sun::star::uno { class XInterface; } |
43 | | namespace com::sun::star::uri { class XUriReference; } |
44 | | |
45 | | namespace { |
46 | | |
47 | 0 | int parseEscaped(std::u16string_view part, sal_Int32 * index) { |
48 | 0 | if (part.size() - *index < 3 || part[*index] != '%') { |
49 | 0 | return -1; |
50 | 0 | } |
51 | 0 | int n1 = o3tl::convertToHex<int>(part[*index + 1]); |
52 | 0 | int n2 = o3tl::convertToHex<int>(part[*index + 2]); |
53 | 0 | if (n1 < 0 || n2 < 0) { |
54 | 0 | return -1; |
55 | 0 | } |
56 | 0 | *index += 3; |
57 | 0 | return (n1 << 4) | n2; |
58 | 0 | } |
59 | | |
60 | | OUString parsePart( |
61 | | std::u16string_view part, bool namePart, sal_Int32 * index) |
62 | 0 | { |
63 | 0 | OUStringBuffer buf(64); |
64 | 0 | while (o3tl::make_unsigned(*index) < part.size()) { |
65 | 0 | sal_Unicode c = part[*index]; |
66 | 0 | if (namePart ? c == '?' : c == '&' || c == '=') { |
67 | 0 | break; |
68 | 0 | } else if (c == '%') { |
69 | 0 | sal_Int32 i = *index; |
70 | 0 | int n = parseEscaped(part, &i); |
71 | 0 | if (n >= 0 && n <= 0x7F) { |
72 | 0 | buf.append(static_cast< sal_Unicode >(n)); |
73 | 0 | } else if (n >= 0xC0 && n <= 0xFC) { |
74 | 0 | sal_Int32 encoded; |
75 | 0 | int shift; |
76 | 0 | sal_Int32 min; |
77 | 0 | if (n <= 0xDF) { |
78 | 0 | encoded = (n & 0x1F) << 6; |
79 | 0 | shift = 0; |
80 | 0 | min = 0x80; |
81 | 0 | } else if (n <= 0xEF) { |
82 | 0 | encoded = (n & 0x0F) << 12; |
83 | 0 | shift = 6; |
84 | 0 | min = 0x800; |
85 | 0 | } else if (n <= 0xF7) { |
86 | 0 | encoded = (n & 0x07) << 18; |
87 | 0 | shift = 12; |
88 | 0 | min = 0x10000; |
89 | 0 | } else if (n <= 0xFB) { |
90 | 0 | encoded = (n & 0x03) << 24; |
91 | 0 | shift = 18; |
92 | 0 | min = 0x200000; |
93 | 0 | } else { |
94 | 0 | encoded = 0; |
95 | 0 | shift = 24; |
96 | 0 | min = 0x4000000; |
97 | 0 | } |
98 | 0 | bool utf8 = true; |
99 | 0 | for (; shift >= 0; shift -= 6) { |
100 | 0 | n = parseEscaped(part, &i); |
101 | 0 | if (n < 0x80 || n > 0xBF) { |
102 | 0 | utf8 = false; |
103 | 0 | break; |
104 | 0 | } |
105 | 0 | encoded |= (n & 0x3F) << shift; |
106 | 0 | } |
107 | 0 | if (!utf8 || !rtl::isUnicodeScalarValue(encoded) |
108 | 0 | || encoded < min) |
109 | 0 | { |
110 | 0 | break; |
111 | 0 | } |
112 | 0 | buf.appendUtf32(encoded); |
113 | 0 | } else { |
114 | 0 | break; |
115 | 0 | } |
116 | 0 | *index = i; |
117 | 0 | } else { |
118 | 0 | buf.append(c); |
119 | 0 | ++*index; |
120 | 0 | } |
121 | 0 | } |
122 | 0 | return buf.makeStringAndClear(); |
123 | 0 | } |
124 | | |
125 | 0 | OUString encodeNameOrParamFragment(OUString const & fragment) { |
126 | 0 | static constexpr auto nameOrParamFragment = rtl::createUriCharClass( |
127 | 0 | u8"!$'()*+,-.0123456789:;@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]_abcdefghijklmnopqrstuvwxyz~"); |
128 | 0 | return rtl::Uri::encode( |
129 | 0 | fragment, nameOrParamFragment.data(), rtl_UriEncodeIgnoreEscapes, |
130 | 0 | RTL_TEXTENCODING_UTF8); |
131 | 0 | } |
132 | | |
133 | 0 | bool parseSchemeSpecificPart(std::u16string_view part) { |
134 | 0 | size_t len = part.size(); |
135 | 0 | sal_Int32 i = 0; |
136 | 0 | if (parsePart(part, true, &i).isEmpty() || part[0] == '/') { |
137 | 0 | return false; |
138 | 0 | } |
139 | 0 | if (o3tl::make_unsigned(i) == len) { |
140 | 0 | return true; |
141 | 0 | } |
142 | 0 | for (;;) { |
143 | 0 | ++i; // skip '?' or '&' |
144 | 0 | if (parsePart(part, false, &i).isEmpty() || o3tl::make_unsigned(i) == len |
145 | 0 | || part[i] != '=') |
146 | 0 | { |
147 | 0 | return false; |
148 | 0 | } |
149 | 0 | ++i; |
150 | 0 | parsePart(part, false, &i); |
151 | 0 | if (o3tl::make_unsigned(i) == len) { |
152 | 0 | return true; |
153 | 0 | } |
154 | 0 | if (part[i] != '&') { |
155 | 0 | return false; |
156 | 0 | } |
157 | 0 | } |
158 | 0 | } |
159 | | |
160 | | class UrlReference: |
161 | | public cppu::WeakImplHelper<css::uri::XVndSunStarScriptUrlReference> |
162 | | { |
163 | | public: |
164 | | UrlReference(OUString const & scheme, OUString const & path): |
165 | 0 | m_base( |
166 | 0 | scheme, false, OUString(), path, false, OUString()) |
167 | 0 | {} |
168 | | |
169 | | UrlReference(const UrlReference&) = delete; |
170 | | UrlReference& operator=(const UrlReference&) = delete; |
171 | | |
172 | | virtual OUString SAL_CALL getUriReference() override |
173 | 0 | { return m_base.getUriReference(); } |
174 | | |
175 | | virtual sal_Bool SAL_CALL isAbsolute() override |
176 | 0 | { return m_base.isAbsolute(); } |
177 | | |
178 | | virtual OUString SAL_CALL getScheme() override |
179 | 0 | { return m_base.getScheme(); } |
180 | | |
181 | | virtual OUString SAL_CALL getSchemeSpecificPart() override |
182 | 0 | { return m_base.getSchemeSpecificPart(); } |
183 | | |
184 | | virtual sal_Bool SAL_CALL isHierarchical() override |
185 | 0 | { return m_base.isHierarchical(); } |
186 | | |
187 | | virtual sal_Bool SAL_CALL hasAuthority() override |
188 | 0 | { return m_base.hasAuthority(); } |
189 | | |
190 | | virtual OUString SAL_CALL getAuthority() override |
191 | 0 | { return m_base.getAuthority(); } |
192 | | |
193 | | virtual OUString SAL_CALL getPath() override |
194 | 0 | { return m_base.getPath(); } |
195 | | |
196 | | virtual sal_Bool SAL_CALL hasRelativePath() override |
197 | 0 | { return m_base.hasRelativePath(); } |
198 | | |
199 | | virtual sal_Int32 SAL_CALL getPathSegmentCount() override |
200 | 0 | { return m_base.getPathSegmentCount(); } |
201 | | |
202 | | virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override |
203 | 0 | { return m_base.getPathSegment(index); } |
204 | | |
205 | | virtual sal_Bool SAL_CALL hasQuery() override |
206 | 0 | { return m_base.hasQuery(); } |
207 | | |
208 | | virtual OUString SAL_CALL getQuery() override |
209 | 0 | { return m_base.getQuery(); } |
210 | | |
211 | | virtual sal_Bool SAL_CALL hasFragment() override |
212 | 0 | { return m_base.hasFragment(); } |
213 | | |
214 | | virtual OUString SAL_CALL getFragment() override |
215 | 0 | { return m_base.getFragment(); } |
216 | | |
217 | | virtual void SAL_CALL setFragment(OUString const & fragment) override |
218 | 0 | { m_base.setFragment(fragment); } |
219 | | |
220 | | virtual void SAL_CALL clearFragment() override |
221 | 0 | { m_base.clearFragment(); } |
222 | | |
223 | | virtual OUString SAL_CALL getName() override; |
224 | | |
225 | | virtual void SAL_CALL setName(OUString const & name) override; |
226 | | |
227 | | virtual sal_Bool SAL_CALL hasParameter(OUString const & key) override; |
228 | | |
229 | | virtual OUString SAL_CALL getParameter(OUString const & key) override; |
230 | | |
231 | | virtual void SAL_CALL setParameter(OUString const & key, OUString const & value) override; |
232 | | |
233 | | private: |
234 | 0 | virtual ~UrlReference() override {} |
235 | | |
236 | | sal_Int32 findParameter(std::u16string_view key) const; |
237 | | |
238 | | stoc::uriproc::UriReference m_base; |
239 | | }; |
240 | | |
241 | 0 | OUString UrlReference::getName() { |
242 | 0 | std::lock_guard g(m_base.m_mutex); |
243 | 0 | sal_Int32 i = 0; |
244 | 0 | return parsePart(m_base.m_path, true, &i); |
245 | 0 | } |
246 | | |
247 | | void SAL_CALL UrlReference::setName(OUString const & name) |
248 | 0 | { |
249 | 0 | if (name.isEmpty()) |
250 | 0 | throw css::lang::IllegalArgumentException( |
251 | 0 | OUString(), *this, 1); |
252 | | |
253 | 0 | std::lock_guard g(m_base.m_mutex); |
254 | 0 | sal_Int32 i = 0; |
255 | 0 | parsePart(m_base.m_path, true, &i); |
256 | |
|
257 | 0 | m_base.m_path = encodeNameOrParamFragment(name) + m_base.m_path.subView(i); |
258 | 0 | } |
259 | | |
260 | | sal_Bool UrlReference::hasParameter(OUString const & key) |
261 | 0 | { |
262 | 0 | std::lock_guard g(m_base.m_mutex); |
263 | 0 | return findParameter(key) >= 0; |
264 | 0 | } |
265 | | |
266 | | OUString UrlReference::getParameter(OUString const & key) |
267 | 0 | { |
268 | 0 | std::lock_guard g(m_base.m_mutex); |
269 | 0 | sal_Int32 i = findParameter(key); |
270 | 0 | return i >= 0 ? parsePart(m_base.m_path, false, &i) : OUString(); |
271 | 0 | } |
272 | | |
273 | | void UrlReference::setParameter(OUString const & key, OUString const & value) |
274 | 0 | { |
275 | 0 | if (key.isEmpty()) |
276 | 0 | throw css::lang::IllegalArgumentException( |
277 | 0 | OUString(), *this, 1); |
278 | | |
279 | 0 | std::lock_guard g(m_base.m_mutex); |
280 | 0 | sal_Int32 i = findParameter(key); |
281 | 0 | bool bExistent = ( i>=0 ); |
282 | 0 | if (!bExistent) { |
283 | 0 | i = m_base.m_path.getLength(); |
284 | 0 | } |
285 | |
|
286 | 0 | OUStringBuffer newPath(128); |
287 | 0 | newPath.append(m_base.m_path.subView(0, i)); |
288 | 0 | if (!bExistent) { |
289 | 0 | newPath.append( m_base.m_path.indexOf('?') < 0 ? '?' : '&' ); |
290 | 0 | newPath.append(encodeNameOrParamFragment(key) + "="); |
291 | 0 | } |
292 | 0 | newPath.append(encodeNameOrParamFragment(value)); |
293 | 0 | if (bExistent) { |
294 | | /*oldValue = */ |
295 | 0 | parsePart(m_base.m_path, false, &i); // skip key |
296 | 0 | newPath.append(m_base.m_path.subView(i)); |
297 | 0 | } |
298 | |
|
299 | 0 | m_base.m_path = newPath.makeStringAndClear(); |
300 | 0 | } |
301 | | |
302 | 0 | sal_Int32 UrlReference::findParameter(std::u16string_view key) const { |
303 | 0 | sal_Int32 i = 0; |
304 | 0 | parsePart(m_base.m_path, true, &i); // skip name |
305 | 0 | for (;;) { |
306 | 0 | if (i == m_base.m_path.getLength()) { |
307 | 0 | return -1; |
308 | 0 | } |
309 | 0 | ++i; // skip '?' or '&' |
310 | 0 | OUString k = parsePart(m_base.m_path, false, &i); |
311 | 0 | ++i; // skip '=' |
312 | 0 | if (k == key) { |
313 | 0 | return i; |
314 | 0 | } |
315 | 0 | parsePart(m_base.m_path, false, &i); // skip value |
316 | 0 | } |
317 | 0 | } |
318 | | |
319 | | class Parser: |
320 | | public cppu::WeakImplHelper< |
321 | | css::lang::XServiceInfo, css::uri::XUriSchemeParser> |
322 | | { |
323 | | public: |
324 | 0 | Parser() {} |
325 | | |
326 | | Parser(const Parser&) = delete; |
327 | | Parser& operator=(const Parser&) = delete; |
328 | | |
329 | | virtual OUString SAL_CALL getImplementationName() override; |
330 | | |
331 | | virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override; |
332 | | |
333 | | virtual css::uno::Sequence< OUString > SAL_CALL |
334 | | getSupportedServiceNames() override; |
335 | | |
336 | | virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL |
337 | | parse( |
338 | | OUString const & scheme, OUString const & schemeSpecificPart) override; |
339 | | |
340 | | private: |
341 | 0 | virtual ~Parser() override {} |
342 | | }; |
343 | | |
344 | | OUString Parser::getImplementationName() |
345 | 0 | { |
346 | 0 | return u"com.sun.star.comp.uri.UriSchemeParser_vndDOTsunDOTstarDOTscript"_ustr; |
347 | 0 | } |
348 | | |
349 | | sal_Bool Parser::supportsService(OUString const & serviceName) |
350 | 0 | { |
351 | 0 | return cppu::supportsService(this, serviceName); |
352 | 0 | } |
353 | | |
354 | | css::uno::Sequence< OUString > Parser::getSupportedServiceNames() |
355 | 0 | { |
356 | 0 | return { u"com.sun.star.uri.UriSchemeParser_vndDOTsunDOTstarDOTscript"_ustr }; |
357 | 0 | } |
358 | | |
359 | | css::uno::Reference< css::uri::XUriReference > |
360 | | Parser::parse( |
361 | | OUString const & scheme, OUString const & schemeSpecificPart) |
362 | 0 | { |
363 | 0 | if (!parseSchemeSpecificPart(schemeSpecificPart)) { |
364 | 0 | return nullptr; |
365 | 0 | } |
366 | 0 | return new UrlReference(scheme, schemeSpecificPart); |
367 | 0 | } |
368 | | |
369 | | } |
370 | | |
371 | | extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* |
372 | | com_sun_star_comp_uri_UriSchemeParser_vndDOTsunDOTstarDOTscript_get_implementation(css::uno::XComponentContext*, |
373 | | css::uno::Sequence<css::uno::Any> const &) |
374 | 0 | { |
375 | | //TODO: single instance |
376 | 0 | return ::cppu::acquire(new Parser()); |
377 | 0 | } |
378 | | |
379 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |