1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_stoc.hxx"
26 
27 #include "stocservices.hxx"
28 
29 #include "UriReference.hxx"
30 #include "supportsService.hxx"
31 
32 #include "com/sun/star/lang/IllegalArgumentException.hpp"
33 #include "com/sun/star/lang/XServiceInfo.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/XInterface.hpp"
38 #include "com/sun/star/uri/XUriReference.hpp"
39 #include "com/sun/star/uri/XUriSchemeParser.hpp"
40 #include "com/sun/star/uri/XVndSunStarScriptUrlReference.hpp"
41 #include "cppuhelper/implbase1.hxx"
42 #include "cppuhelper/implbase2.hxx"
43 #include "cppuhelper/weak.hxx"
44 #include "osl/mutex.hxx"
45 #include "rtl/uri.hxx"
46 #include "rtl/ustrbuf.hxx"
47 #include "rtl/ustring.hxx"
48 #include "sal/types.h"
49 
50 #include <new>
51 
52 namespace css = com::sun::star;
53 
54 namespace {
55 
getHexWeight(sal_Unicode c)56 int getHexWeight(sal_Unicode c) {
57     return c >= '0' && c <= '9' ? static_cast< int >(c - '0')
58         : c >= 'A' && c <= 'F' ? static_cast< int >(c - 'A' + 10)
59         : c >= 'a' && c <= 'f' ? static_cast< int >(c - 'a' + 10)
60         : -1;
61 }
62 
parseEscaped(rtl::OUString const & part,sal_Int32 * index)63 int parseEscaped(rtl::OUString const & part, sal_Int32 * index) {
64     if (part.getLength() - *index < 3 || part[*index] != '%') {
65         return -1;
66     }
67     int n1 = getHexWeight(part[*index + 1]);
68     int n2 = getHexWeight(part[*index + 2]);
69     if (n1 < 0 || n2 < 0) {
70         return -1;
71     }
72     *index += 3;
73     return (n1 << 4) | n2;
74 }
75 
parsePart(rtl::OUString const & part,bool namePart,sal_Int32 * index)76 rtl::OUString parsePart(
77     rtl::OUString const & part, bool namePart, sal_Int32 * index)
78 {
79     rtl::OUStringBuffer buf;
80     while (*index < part.getLength()) {
81         sal_Unicode c = part[*index];
82         if (namePart ? c == '?' : c == '&' || c == '=') {
83             break;
84         } else if (c == '%') {
85             sal_Int32 i = *index;
86             int n = parseEscaped(part, &i);
87             if (n >= 0 && n <= 0x7F) {
88                 buf.append(static_cast< sal_Unicode >(n));
89             } else if (n >= 0xC0 && n <= 0xFC) {
90                 sal_Int32 encoded;
91                 int shift;
92                 sal_Int32 min;
93                 if (n <= 0xDF) {
94                     encoded = (n & 0x1F) << 6;
95                     shift = 0;
96                     min = 0x80;
97                 } else if (n <= 0xEF) {
98                     encoded = (n & 0x0F) << 12;
99                     shift = 6;
100                     min = 0x800;
101                 } else if (n <= 0xF7) {
102                     encoded = (n & 0x07) << 18;
103                     shift = 12;
104                     min = 0x10000;
105                 } else if (n <= 0xFB) {
106                     encoded = (n & 0x03) << 24;
107                     shift = 18;
108                     min = 0x200000;
109                 } else {
110                     encoded = 0;
111                     shift = 24;
112                     min = 0x4000000;
113                 }
114                 bool utf8 = true;
115                 for (; shift >= 0; shift -= 6) {
116                     n = parseEscaped(part, &i);
117                     if (n < 0x80 || n > 0xBF) {
118                         utf8 = false;
119                         break;
120                     }
121                     encoded |= (n & 0x3F) << shift;
122                 }
123                 if (!utf8 || encoded < min
124                     || (encoded >= 0xD800 && encoded <= 0xDFFF)
125                     || encoded > 0x10FFFF)
126                 {
127                     break;
128                 }
129                 if (encoded <= 0xFFFF) {
130                     buf.append(static_cast< sal_Unicode >(encoded));
131                 } else {
132                     buf.append(static_cast< sal_Unicode >(
133                         (encoded >> 10) | 0xD800));
134                     buf.append(static_cast< sal_Unicode >(
135                         (encoded & 0x3FF) | 0xDC00));
136                 }
137             } else {
138                 break;
139             }
140             *index = i;
141         } else {
142             buf.append(c);
143             ++*index;
144         }
145     }
146     return buf.makeStringAndClear();
147 }
148 
149 namespace
150 {
encodeNameOrParamFragment(rtl::OUString const & fragment)151     static rtl::OUString encodeNameOrParamFragment( rtl::OUString const & fragment )
152     {
153         static sal_Bool const aCharClass[] =
154         { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* NameOrParamFragment */
155           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156           0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* !"#$%&'()*+,-./*/
157           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, /*0123456789:;<=>?*/
158           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*@ABCDEFGHIJKLMNO*/
159           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, /*PQRSTUVWXYZ[\]^_*/
160           0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*`abcdefghijklmno*/
161           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0  /*pqrstuvwxyz{|}~ */
162         };
163 
164         return rtl::Uri::encode(
165             fragment,
166             aCharClass,
167             rtl_UriEncodeIgnoreEscapes,
168             RTL_TEXTENCODING_UTF8
169         );
170     }
171 }
172 
parseSchemeSpecificPart(rtl::OUString const & part)173 bool parseSchemeSpecificPart(rtl::OUString const & part) {
174     sal_Int32 len = part.getLength();
175     sal_Int32 i = 0;
176     if (parsePart(part, true, &i).getLength() == 0 || part[0] == '/') {
177         return false;
178     }
179     if (i == len) {
180         return true;
181     }
182     for (;;) {
183         ++i; // skip '?' or '&'
184         if (parsePart(part, false, &i).getLength() == 0 || i == len
185             || part[i] != '=')
186         {
187             return false;
188         }
189         ++i;
190         parsePart(part, false, &i);
191         if (i == len) {
192             return true;
193         }
194         if (part[i] != '&') {
195             return false;
196         }
197     }
198 }
199 
200 class UrlReference:
201     public cppu::WeakImplHelper1< css::uri::XVndSunStarScriptUrlReference >
202 {
203 public:
UrlReference(rtl::OUString const & scheme,rtl::OUString const & path)204     UrlReference(rtl::OUString const & scheme, rtl::OUString const & path):
205         m_base(
206             scheme, false, false, rtl::OUString(), path, false, rtl::OUString())
207     {}
208 
getUriReference()209     virtual rtl::OUString SAL_CALL getUriReference()
210         throw (com::sun::star::uno::RuntimeException)
211     { return m_base.getUriReference(); }
212 
isAbsolute()213     virtual sal_Bool SAL_CALL isAbsolute()
214         throw (com::sun::star::uno::RuntimeException)
215     { return m_base.isAbsolute(); }
216 
getScheme()217     virtual rtl::OUString SAL_CALL getScheme()
218         throw (com::sun::star::uno::RuntimeException)
219     { return m_base.getScheme(); }
220 
getSchemeSpecificPart()221     virtual rtl::OUString SAL_CALL getSchemeSpecificPart()
222         throw (com::sun::star::uno::RuntimeException)
223     { return m_base.getSchemeSpecificPart(); }
224 
isHierarchical()225     virtual sal_Bool SAL_CALL isHierarchical()
226         throw (com::sun::star::uno::RuntimeException)
227     { return m_base.isHierarchical(); }
228 
hasAuthority()229     virtual sal_Bool SAL_CALL hasAuthority()
230         throw (com::sun::star::uno::RuntimeException)
231     { return m_base.hasAuthority(); }
232 
getAuthority()233     virtual rtl::OUString SAL_CALL getAuthority()
234         throw (com::sun::star::uno::RuntimeException)
235     { return m_base.getAuthority(); }
236 
getPath()237     virtual rtl::OUString SAL_CALL getPath()
238         throw (com::sun::star::uno::RuntimeException)
239     { return m_base.getPath(); }
240 
hasRelativePath()241     virtual sal_Bool SAL_CALL hasRelativePath()
242         throw (com::sun::star::uno::RuntimeException)
243     { return m_base.hasRelativePath(); }
244 
getPathSegmentCount()245     virtual sal_Int32 SAL_CALL getPathSegmentCount()
246         throw (com::sun::star::uno::RuntimeException)
247     { return m_base.getPathSegmentCount(); }
248 
getPathSegment(sal_Int32 index)249     virtual rtl::OUString SAL_CALL getPathSegment(sal_Int32 index)
250         throw (com::sun::star::uno::RuntimeException)
251     { return m_base.getPathSegment(index); }
252 
hasQuery()253     virtual sal_Bool SAL_CALL hasQuery()
254         throw (com::sun::star::uno::RuntimeException)
255     { return m_base.hasQuery(); }
256 
getQuery()257     virtual rtl::OUString SAL_CALL getQuery()
258         throw (com::sun::star::uno::RuntimeException)
259     { return m_base.getQuery(); }
260 
hasFragment()261     virtual sal_Bool SAL_CALL hasFragment()
262         throw (com::sun::star::uno::RuntimeException)
263     { return m_base.hasFragment(); }
264 
getFragment()265     virtual rtl::OUString SAL_CALL getFragment()
266         throw (com::sun::star::uno::RuntimeException)
267     { return m_base.getFragment(); }
268 
setFragment(rtl::OUString const & fragment)269     virtual void SAL_CALL setFragment(rtl::OUString const & fragment)
270         throw (com::sun::star::uno::RuntimeException)
271     { m_base.setFragment(fragment); }
272 
clearFragment()273     virtual void SAL_CALL clearFragment()
274         throw (com::sun::star::uno::RuntimeException)
275     { m_base.clearFragment(); }
276 
277     virtual rtl::OUString SAL_CALL getName() throw (css::uno::RuntimeException);
278 
279     virtual void SAL_CALL setName(rtl::OUString const & name)
280         throw (css::uno::RuntimeException, css::lang::IllegalArgumentException);
281 
282     virtual sal_Bool SAL_CALL hasParameter(rtl::OUString const & key)
283         throw (css::uno::RuntimeException);
284 
285     virtual rtl::OUString SAL_CALL getParameter(rtl::OUString const & key)
286         throw (css::uno::RuntimeException);
287 
288     virtual void SAL_CALL setParameter(rtl::OUString const & key, rtl::OUString const & value)
289         throw (css::uno::RuntimeException, css::lang::IllegalArgumentException);
290 
291 private:
292     UrlReference(UrlReference &); // not implemented
293     void operator =(UrlReference); // not implemented
294 
~UrlReference()295     virtual ~UrlReference() {}
296 
297     sal_Int32 findParameter(rtl::OUString const & key);
298 
299     stoc::uriproc::UriReference m_base;
300 };
301 
getName()302 rtl::OUString UrlReference::getName() throw (css::uno::RuntimeException) {
303     osl::MutexGuard g(m_base.m_mutex);
304     sal_Int32 i = 0;
305     return parsePart(m_base.m_path, true, &i);
306 }
307 
setName(rtl::OUString const & name)308 void SAL_CALL UrlReference::setName(rtl::OUString const & name) throw (css::uno::RuntimeException, css::lang::IllegalArgumentException)
309 {
310     if (name.getLength() == 0)
311         throw css::lang::IllegalArgumentException(
312             ::rtl::OUString(), *this, 1);
313 
314     osl::MutexGuard g(m_base.m_mutex);
315     sal_Int32 i = 0;
316     parsePart(m_base.m_path, true, &i);
317 
318     rtl::OUStringBuffer newPath;
319     newPath.append(encodeNameOrParamFragment(name));
320     newPath.append(m_base.m_path.copy(i));
321     m_base.m_path = newPath.makeStringAndClear();
322 }
323 
hasParameter(rtl::OUString const & key)324 sal_Bool UrlReference::hasParameter(rtl::OUString const & key)
325     throw (css::uno::RuntimeException)
326 {
327     osl::MutexGuard g(m_base.m_mutex);
328     return findParameter(key) >= 0;
329 }
330 
getParameter(rtl::OUString const & key)331 rtl::OUString UrlReference::getParameter(rtl::OUString const & key)
332     throw (css::uno::RuntimeException)
333 {
334     osl::MutexGuard g(m_base.m_mutex);
335     sal_Int32 i = findParameter(key);
336     return i >= 0 ? parsePart(m_base.m_path, false, &i) : rtl::OUString();
337 }
338 
setParameter(rtl::OUString const & key,rtl::OUString const & value)339 void UrlReference::setParameter(rtl::OUString const & key, rtl::OUString const & value)
340     throw (css::uno::RuntimeException, css::lang::IllegalArgumentException)
341 {
342     if (key.getLength() == 0)
343         throw css::lang::IllegalArgumentException(
344             ::rtl::OUString(), *this, 1);
345 
346     osl::MutexGuard g(m_base.m_mutex);
347     sal_Int32 i = findParameter(key);
348     bool bExistent = ( i>=0 );
349     if (!bExistent) {
350         i = m_base.m_path.getLength();
351     }
352 
353     rtl::OUStringBuffer newPath;
354     newPath.append(m_base.m_path.copy(0, i));
355     if (!bExistent) {
356         newPath.append(sal_Unicode(m_base.m_path.indexOf('?') < 0 ? '?' : '&'));
357         newPath.append(encodeNameOrParamFragment(key));
358         newPath.append(sal_Unicode('='));
359     }
360     newPath.append(encodeNameOrParamFragment(value));
361     if (bExistent) {
362         /*oldValue = */
363         parsePart(m_base.m_path, false, &i); // skip key
364         newPath.append(m_base.m_path.copy(i));
365     }
366 
367     m_base.m_path = newPath.makeStringAndClear();
368 }
369 
findParameter(rtl::OUString const & key)370 sal_Int32 UrlReference::findParameter(rtl::OUString const & key) {
371     sal_Int32 i = 0;
372     parsePart(m_base.m_path, true, &i); // skip name
373     for (;;) {
374         if (i == m_base.m_path.getLength()) {
375             return -1;
376         }
377         ++i; // skip '?' or '&'
378         rtl::OUString k = parsePart(m_base.m_path, false, &i);
379         ++i; // skip '='
380         if (k == key) {
381             return i;
382         }
383         parsePart(m_base.m_path, false, &i); // skip value
384     }
385 }
386 
387 class Parser: public cppu::WeakImplHelper2<
388     css::lang::XServiceInfo, css::uri::XUriSchemeParser >
389 {
390 public:
Parser()391     Parser() {}
392 
393     virtual rtl::OUString SAL_CALL getImplementationName()
394         throw (css::uno::RuntimeException);
395 
396     virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & serviceName)
397         throw (css::uno::RuntimeException);
398 
399     virtual css::uno::Sequence< rtl::OUString > SAL_CALL
400     getSupportedServiceNames() throw (css::uno::RuntimeException);
401 
402     virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
403     parse(
404         rtl::OUString const & scheme, rtl::OUString const & schemeSpecificPart)
405         throw (css::uno::RuntimeException);
406 
407 private:
408     Parser(Parser &); // not implemented
409     void operator =(Parser); // not implemented
410 
~Parser()411     virtual ~Parser() {}
412 };
413 
getImplementationName()414 rtl::OUString Parser::getImplementationName()
415     throw (css::uno::RuntimeException)
416 {
417     return stoc_services::UriSchemeParser_vndDOTsunDOTstarDOTscript::
418         getImplementationName();
419 }
420 
supportsService(rtl::OUString const & serviceName)421 sal_Bool Parser::supportsService(rtl::OUString const & serviceName)
422     throw (css::uno::RuntimeException)
423 {
424     return stoc::uriproc::supportsService(
425         getSupportedServiceNames(), serviceName);
426 }
427 
getSupportedServiceNames()428 css::uno::Sequence< rtl::OUString > Parser::getSupportedServiceNames()
429     throw (css::uno::RuntimeException)
430 {
431     return stoc_services::UriSchemeParser_vndDOTsunDOTstarDOTscript::
432         getSupportedServiceNames();
433 }
434 
435 css::uno::Reference< css::uri::XUriReference >
parse(rtl::OUString const & scheme,rtl::OUString const & schemeSpecificPart)436 Parser::parse(
437     rtl::OUString const & scheme, rtl::OUString const & schemeSpecificPart)
438     throw (css::uno::RuntimeException)
439 {
440     if (!parseSchemeSpecificPart(schemeSpecificPart)) {
441         return 0;
442     }
443     try {
444         return new UrlReference(scheme, schemeSpecificPart);
445     } catch (std::bad_alloc &) {
446         throw css::uno::RuntimeException(
447             rtl::OUString::createFromAscii("std::bad_alloc"), 0);
448     }
449 }
450 
451 }
452 
453 namespace stoc_services {
454 namespace UriSchemeParser_vndDOTsunDOTstarDOTscript {
455 
create(css::uno::Reference<css::uno::XComponentContext> const &)456 css::uno::Reference< css::uno::XInterface > create(
457     css::uno::Reference< css::uno::XComponentContext > const &)
458     SAL_THROW((css::uno::Exception))
459 {
460     //TODO: single instance
461     try {
462         return static_cast< cppu::OWeakObject * >(new Parser);
463     } catch (std::bad_alloc &) {
464         throw css::uno::RuntimeException(
465             rtl::OUString::createFromAscii("std::bad_alloc"), 0);
466     }
467 }
468 
getImplementationName()469 rtl::OUString getImplementationName() {
470     return rtl::OUString::createFromAscii(
471         "com.sun.star.comp.uri.UriSchemeParser_vndDOTsunDOTstarDOTscript");
472 }
473 
getSupportedServiceNames()474 css::uno::Sequence< rtl::OUString > getSupportedServiceNames() {
475     css::uno::Sequence< rtl::OUString > s(1);
476     s[0] = rtl::OUString::createFromAscii(
477         "com.sun.star.uri.UriSchemeParser_vndDOTsunDOTstarDOTscript");
478     return s;
479 }
480 
481 } }
482