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