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_svl.hxx"
26
27 #include "sal/config.h"
28
29 #include <cstddef>
30
31 #include "com/sun/star/lang/Locale.hpp"
32 #include "com/sun/star/lang/XComponent.hpp"
33 #include "com/sun/star/lang/XMultiComponentFactory.hpp"
34 #include "com/sun/star/lang/XMultiServiceFactory.hpp"
35 #include "com/sun/star/ucb/Command.hpp"
36 #include "com/sun/star/ucb/CommandAbortedException.hpp"
37 #include "com/sun/star/ucb/IllegalIdentifierException.hpp"
38 #include "com/sun/star/ucb/XCommandProcessor.hpp"
39 #include "com/sun/star/ucb/XContent.hpp"
40 #include "com/sun/star/ucb/XContentIdentifier.hpp"
41 #include "com/sun/star/ucb/XContentProvider.hpp"
42 #include "com/sun/star/ucb/XContentProviderManager.hpp"
43 #include "com/sun/star/uno/Any.hxx"
44 #include "com/sun/star/uno/Exception.hpp"
45 #include "com/sun/star/uno/Reference.hxx"
46 #include "com/sun/star/uno/RuntimeException.hpp"
47 #include "com/sun/star/uno/Sequence.hxx"
48 #include "com/sun/star/uno/XComponentContext.hpp"
49 #include "com/sun/star/uri/XUriReference.hpp"
50 #include "cppuhelper/bootstrap.hxx"
51 #include "cppuhelper/implbase1.hxx"
52 #include "cppuhelper/implbase2.hxx"
53 #include "testshl/simpleheader.hxx"
54 #include "osl/diagnose.h"
55 #include "rtl/strbuf.hxx"
56 #include "rtl/string.h"
57 #include "rtl/string.hxx"
58 #include "rtl/textenc.h"
59 #include "rtl/ustring.h"
60 #include "rtl/ustring.hxx"
61 #include "sal/types.h"
62 #include "tools/solar.h"
63 #include "unotools/charclass.hxx"
64
65 #include "urihelper.hxx"
66
67 // This test needs a UNO component context that supports various services (the
68 // UCB, an UriReferenceFactory, ...), so it is best executed within an OOo
69 // installation.
70
71 namespace com { namespace sun { namespace star { namespace ucb {
72 class XCommandEnvironment;
73 class XContentEventListener;
74 } } } }
75
76 namespace {
77
78 namespace css = com::sun::star;
79
80 // This class only implements that subset of functionality of a proper
81 // css::ucb::Content that is known to be needed here:
82 class Content:
83 public cppu::WeakImplHelper2<
84 css::ucb::XContent, css::ucb::XCommandProcessor >
85 {
86 public:
87 explicit Content(
88 css::uno::Reference< css::ucb::XContentIdentifier > const & identifier);
89
90 virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL
getIdentifier()91 getIdentifier() throw (css::uno::RuntimeException) {
92 return m_identifier;
93 }
94
getContentType()95 virtual rtl::OUString SAL_CALL getContentType()
96 throw (css::uno::RuntimeException)
97 {
98 return rtl::OUString();
99 }
100
addContentEventListener(css::uno::Reference<css::ucb::XContentEventListener> const &)101 virtual void SAL_CALL addContentEventListener(
102 css::uno::Reference< css::ucb::XContentEventListener > const &)
103 throw (css::uno::RuntimeException)
104 {}
105
removeContentEventListener(css::uno::Reference<css::ucb::XContentEventListener> const &)106 virtual void SAL_CALL removeContentEventListener(
107 css::uno::Reference< css::ucb::XContentEventListener > const &)
108 throw (css::uno::RuntimeException)
109 {}
110
createCommandIdentifier()111 virtual sal_Int32 SAL_CALL createCommandIdentifier()
112 throw (css::uno::RuntimeException)
113 {
114 return 0;
115 }
116
117 virtual css::uno::Any SAL_CALL execute(
118 css::ucb::Command const & command, sal_Int32 commandId,
119 css::uno::Reference< css::ucb::XCommandEnvironment > const &)
120 throw (
121 css::uno::Exception, css::ucb::CommandAbortedException,
122 css::uno::RuntimeException);
123
abort(sal_Int32)124 virtual void SAL_CALL abort(sal_Int32) throw (css::uno::RuntimeException) {}
125
126 private:
127 static char const m_prefix[];
128
129 css::uno::Reference< css::ucb::XContentIdentifier > m_identifier;
130 };
131
132 char const Content::m_prefix[] = "test:";
133
Content(css::uno::Reference<css::ucb::XContentIdentifier> const & identifier)134 Content::Content(
135 css::uno::Reference< css::ucb::XContentIdentifier > const & identifier):
136 m_identifier(identifier)
137 {
138 OSL_ASSERT(m_identifier.is());
139 rtl::OUString uri(m_identifier->getContentIdentifier());
140 if (!uri.matchIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM(m_prefix))
141 || uri.indexOf('#', RTL_CONSTASCII_LENGTH(m_prefix)) != -1)
142 {
143 throw css::ucb::IllegalIdentifierException();
144 }
145 }
146
execute(css::ucb::Command const & command,sal_Int32,css::uno::Reference<css::ucb::XCommandEnvironment> const &)147 css::uno::Any Content::execute(
148 css::ucb::Command const & command, sal_Int32,
149 css::uno::Reference< css::ucb::XCommandEnvironment > const &)
150 throw (
151 css::uno::Exception, css::ucb::CommandAbortedException,
152 css::uno::RuntimeException)
153 {
154 if (!command.Name.equalsAsciiL(
155 RTL_CONSTASCII_STRINGPARAM("getCasePreservingURL")))
156 {
157 throw css::uno::RuntimeException();
158 }
159 // If any non-empty segment starts with anything but '0', '1', or '2', fail;
160 // otherwise, if the last non-empty segment starts with '1', add a final
161 // slash, and if the last non-empty segment starts with '2', remove a final
162 // slash (if any); also, turn the given uri into all-lowercase:
163 rtl::OUString uri(m_identifier->getContentIdentifier());
164 sal_Unicode c = '0';
165 for (sal_Int32 i = RTL_CONSTASCII_LENGTH(m_prefix); i != -1;) {
166 rtl::OUString seg(uri.getToken(0, '/', i));
167 if (seg.getLength() > 0) {
168 c = seg[0];
169 if (c < '0' || c > '2') {
170 throw css::uno::Exception();
171 }
172 }
173 }
174 switch (c) {
175 case '1':
176 uri += rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/"));
177 break;
178 case '2':
179 if (uri.getLength() > 0 && uri[uri.getLength() - 1] == '/') {
180 uri = uri.copy(0, uri.getLength() -1);
181 }
182 break;
183 }
184 return css::uno::makeAny(uri.toAsciiLowerCase());
185 }
186
187 class Provider: public cppu::WeakImplHelper1< css::ucb::XContentProvider > {
188 public:
queryContent(css::uno::Reference<css::ucb::XContentIdentifier> const & identifier)189 virtual css::uno::Reference< css::ucb::XContent > SAL_CALL queryContent(
190 css::uno::Reference< css::ucb::XContentIdentifier > const & identifier)
191 throw (css::ucb::IllegalIdentifierException, css::uno::RuntimeException)
192 {
193 return new Content(identifier);
194 }
195
compareContentIds(css::uno::Reference<css::ucb::XContentIdentifier> const & id1,css::uno::Reference<css::ucb::XContentIdentifier> const & id2)196 virtual sal_Int32 SAL_CALL compareContentIds(
197 css::uno::Reference< css::ucb::XContentIdentifier > const & id1,
198 css::uno::Reference< css::ucb::XContentIdentifier > const & id2)
199 throw (css::uno::RuntimeException)
200 {
201 OSL_ASSERT(id1.is() && id2.is());
202 return
203 id1->getContentIdentifier().compareTo(id2->getContentIdentifier());
204 }
205 };
206
207 class Test: public CppUnit::TestFixture {
208 public:
209 virtual void setUp();
210
211 void finish();
212
213 void testNormalizedMakeRelative();
214
215 void testFindFirstURLInText();
216
217 CPPUNIT_TEST_SUITE(Test);
218 CPPUNIT_TEST(testNormalizedMakeRelative);
219 CPPUNIT_TEST(testFindFirstURLInText);
220 CPPUNIT_TEST(finish);
221 CPPUNIT_TEST_SUITE_END();
222
223 private:
224 static css::uno::Reference< css::uno::XComponentContext > m_context;
225 };
226
setUp()227 void Test::setUp() {
228 // For whatever reason, on W32 it does not work to create/destroy a fresh
229 // component context for each test in Test::setUp/tearDown; therefore, a
230 // single component context is used for all tests and destroyed in the last
231 // pseudo-test "finish":
232 if (!m_context.is()) {
233 m_context = cppu::defaultBootstrap_InitialComponentContext();
234 }
235 }
236
finish()237 void Test::finish() {
238 css::uno::Reference< css::lang::XComponent >(
239 m_context, css::uno::UNO_QUERY_THROW)->dispose();
240 }
241
testNormalizedMakeRelative()242 void Test::testNormalizedMakeRelative() {
243 css::uno::Sequence< css::uno::Any > args(2);
244 args[0] <<= rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Local"));
245 args[1] <<= rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Office"));
246 css::uno::Reference< css::ucb::XContentProviderManager >(
247 (css::uno::Reference< css::lang::XMultiComponentFactory >(
248 m_context->getServiceManager(), css::uno::UNO_QUERY_THROW)->
249 createInstanceWithArgumentsAndContext(
250 rtl::OUString(
251 RTL_CONSTASCII_USTRINGPARAM(
252 "com.sun.star.ucb.UniversalContentBroker")),
253 args, m_context)),
254 css::uno::UNO_QUERY_THROW)->registerContentProvider(
255 new Provider, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("test")),
256 true);
257 struct Test {
258 char const * base;
259 char const * absolute;
260 char const * relative;
261 };
262 static Test const tests[] = {
263 { "hierarchical:/", "mailto:def@a.b.c.", "mailto:def@a.b.c." },
264 { "hierarchical:/", "a/b/c", "a/b/c" },
265 { "hierarchical:/a", "hierarchical:/a/b/c?d#e", "/a/b/c?d#e" },
266 { "hierarchical:/a/", "hierarchical:/a/b/c?d#e", "b/c?d#e" },
267 { "test:/0/0/a", "test:/0/b", "../b" },
268 { "test:/1/1/a", "test:/1/b", "../b" },
269 { "test:/2/2//a", "test:/2/b", "../../b" },
270 { "test:/0a/b", "test:/0A/c#f", "c#f" },
271 { "file:///usr/bin/nonex1/nonex2",
272 "file:///usr/bin/nonex1/nonex3/nonex4", "nonex3/nonex4" },
273 { "file:///usr/bin/nonex1/nonex2#fragmentA",
274 "file:///usr/bin/nonex1/nonex3/nonex4#fragmentB",
275 "nonex3/nonex4#fragmentB" },
276 { "file:///usr/nonex1/nonex2", "file:///usr/nonex3", "../nonex3" },
277 { "file:///c:/windows/nonex1", "file:///c:/nonex2", "../nonex2" },
278 #if defined WNT
279 { "file:///c:/nonex1/nonex2", "file:///C:/nonex1/nonex3/nonex4",
280 "nonex3/nonex4" }
281 #endif
282 };
283 for (std::size_t i = 0; i < sizeof tests / sizeof tests[0]; ++i) {
284 css::uno::Reference< css::uri::XUriReference > ref(
285 URIHelper::normalizedMakeRelative(
286 m_context, rtl::OUString::createFromAscii(tests[i].base),
287 rtl::OUString::createFromAscii(tests[i].absolute)));
288 bool ok = tests[i].relative == 0
289 ? !ref.is()
290 : ref.is() && ref->getUriReference().equalsAscii(tests[i].relative);
291 rtl::OString msg;
292 if (!ok) {
293 rtl::OStringBuffer buf;
294 buf.append('<');
295 buf.append(tests[i].base);
296 buf.append(RTL_CONSTASCII_STRINGPARAM(">, <"));
297 buf.append(tests[i].absolute);
298 buf.append(RTL_CONSTASCII_STRINGPARAM(">: "));
299 if (ref.is()) {
300 buf.append('<');
301 buf.append(
302 rtl::OUStringToOString(
303 ref->getUriReference(), RTL_TEXTENCODING_UTF8));
304 buf.append('>');
305 } else {
306 buf.append(RTL_CONSTASCII_STRINGPARAM("none"));
307 }
308 buf.append(RTL_CONSTASCII_STRINGPARAM(" instead of "));
309 if (tests[i].relative == 0) {
310 buf.append(RTL_CONSTASCII_STRINGPARAM("none"));
311 } else {
312 buf.append('<');
313 buf.append(tests[i].relative);
314 buf.append('>');
315 }
316 msg = buf.makeStringAndClear();
317 }
318 CPPUNIT_ASSERT_MESSAGE(msg.getStr(), ok);
319 }
320 }
321
testFindFirstURLInText()322 void Test::testFindFirstURLInText() {
323 struct Test {
324 char const * input;
325 char const * result;
326 xub_StrLen begin;
327 xub_StrLen end;
328 };
329 static Test const tests[] = {
330 { "...ftp://bla.bla.bla/blubber/...",
331 "ftp://bla.bla.bla/blubber/", 3, 29 },
332 { "..\\ftp://bla.bla.bla/blubber/...", 0, 0, 0 },
333 { "..\\ftp:\\\\bla.bla.bla\\blubber/...",
334 "file://bla.bla.bla/blubber%2F", 7, 29 },
335 { "http://sun.com", "http://sun.com/", 0, 14 },
336 { "http://sun.com/", "http://sun.com/", 0, 15 },
337 { "http://www.xerox.com@www.pcworld.com/go/3990332.htm", 0, 0, 0 },
338 { "ftp://www.xerox.com@www.pcworld.com/go/3990332.htm",
339 "ftp://www.xerox.com@www.pcworld.com/go/3990332.htm", 0, 50 },
340 { "Version.1.2.3", 0, 0, 0 },
341 { "Version:1.2.3", 0, 0, 0 },
342 { "a.b.c", 0, 0, 0 },
343 { "file:///a|...", "file:///a:", 0, 10 },
344 { "file:///a||...", "file:///a%7C%7C", 0, 11 },
345 { "file:///a|/bc#...", "file:///a:/bc", 0, 13 },
346 { "file:///a|/bc#de...", "file:///a:/bc#de", 0, 16 },
347 { "abc.def.ghi,ftp.xxx.yyy/zzz...", "ftp://ftp.xxx.yyy/zzz", 12, 27 },
348 { "abc.def.ghi,Ftp.xxx.yyy/zzz...", "ftp://Ftp.xxx.yyy/zzz", 12, 27 },
349 { "abc.def.ghi,www.xxx.yyy...", "http://www.xxx.yyy/", 12, 23 },
350 { "abc.def.ghi,wwww.xxx.yyy...", 0, 0, 0 },
351 { "abc.def.ghi,wWW.xxx.yyy...", "http://wWW.xxx.yyy/", 12, 23 },
352 { "Bla {mailto.me@abc.def.g.h.i}...",
353 "mailto:%7Bmailto.me@abc.def.g.h.i", 4, 28 },
354 { "abc@def@ghi", 0, 0, 0 },
355 { "lala@sun.com", "mailto:lala@sun.com", 0, 12 },
356 { "1lala@sun.com", "mailto:1lala@sun.com", 0, 13 },
357 { "aaa_bbb@xxx.yy", "mailto:aaa_bbb@xxx.yy", 0, 14 },
358 { "{a:\\bla/bla/bla...}", "file:///a:/bla/bla/bla", 1, 15 },
359 { "#b:/c/d#e#f#", "file:///b:/c/d", 1, 7 },
360 { "a:/", "file:///a:/", 0, 3 },
361 { ".component:", 0, 0, 0 },
362 { ".uno:", 0, 0, 0 },
363 { "cid:", 0, 0, 0 },
364 { "data:", 0, 0, 0 },
365 { "db:", 0, 0, 0 },
366 { "file:", 0, 0, 0 },
367 { "ftp:", 0, 0, 0 },
368 { "http:", 0, 0, 0 },
369 { "https:", 0, 0, 0 },
370 { "imap:", 0, 0, 0 },
371 { "javascript:", 0, 0, 0 },
372 { "ldap:", 0, 0, 0 },
373 { "macro:", 0, 0, 0 },
374 { "mailto:", 0, 0, 0 },
375 { "news:", 0, 0, 0 },
376 { "out:", 0, 0, 0 },
377 { "pop3:", 0, 0, 0 },
378 { "private:", 0, 0, 0 },
379 { "slot:", 0, 0, 0 },
380 { "staroffice.component:", 0, 0, 0 },
381 { "staroffice.db:", 0, 0, 0 },
382 { "staroffice.factory:", 0, 0, 0 },
383 { "staroffice.helpid:", 0, 0, 0 },
384 { "staroffice.java:", 0, 0, 0 },
385 { "staroffice.macro:", 0, 0, 0 },
386 { "staroffice.out:", 0, 0, 0 },
387 { "staroffice.pop3:", 0, 0, 0 },
388 { "staroffice.private:", 0, 0, 0 },
389 { "staroffice.searchfolder:", 0, 0, 0 },
390 { "staroffice.slot:", 0, 0, 0 },
391 { "staroffice.trashcan:", 0, 0, 0 },
392 { "staroffice.uno:", 0, 0, 0 },
393 { "staroffice.vim:", 0, 0, 0 },
394 { "staroffice:", 0, 0, 0 },
395 { "vim:", 0, 0, 0 },
396 { "vnd.sun.star.cmd:", 0, 0, 0 },
397 { "vnd.sun.star.help:", 0, 0, 0 },
398 { "vnd.sun.star.hier:", 0, 0, 0 },
399 { "vnd.sun.star.odma:", 0, 0, 0 },
400 { "vnd.sun.star.pkg:", 0, 0, 0 },
401 { "vnd.sun.star.script:", 0, 0, 0 },
402 { "vnd.sun.star.webdav:", 0, 0, 0 },
403 { "vnd.sun.star.wfs:", 0, 0, 0 },
404 { "generic:path", 0, 0, 0 },
405 { "wfs:", 0, 0, 0 }
406 };
407 CharClass charClass(
408 css::uno::Reference< css::lang::XMultiServiceFactory >(
409 m_context->getServiceManager(), css::uno::UNO_QUERY_THROW),
410 com::sun::star::lang::Locale(
411 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("en")),
412 rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("US")), rtl::OUString()));
413 for (std::size_t i = 0; i < sizeof tests / sizeof tests[0]; ++i) {
414 rtl::OUString input(rtl::OUString::createFromAscii(tests[i].input));
415 xub_StrLen begin = 0;
416 xub_StrLen end = static_cast< xub_StrLen >(input.getLength());
417 rtl::OUString result(
418 URIHelper::FindFirstURLInText(input, begin, end, charClass));
419 bool ok = tests[i].result == 0
420 ? (result.getLength() == 0 && begin == input.getLength()
421 && end == input.getLength())
422 : (result.equalsAscii(tests[i].result) && begin == tests[i].begin
423 && end == tests[i].end);
424 rtl::OString msg;
425 if (!ok) {
426 rtl::OStringBuffer buf;
427 buf.append('"');
428 buf.append(tests[i].input);
429 buf.append(RTL_CONSTASCII_STRINGPARAM("\" -> "));
430 buf.append(tests[i].result == 0 ? "none" : tests[i].result);
431 buf.append(RTL_CONSTASCII_STRINGPARAM(" ("));
432 buf.append(static_cast< sal_Int32 >(tests[i].begin));
433 buf.append(RTL_CONSTASCII_STRINGPARAM(", "));
434 buf.append(static_cast< sal_Int32 >(tests[i].end));
435 buf.append(')');
436 buf.append(RTL_CONSTASCII_STRINGPARAM(" != "));
437 buf.append(rtl::OUStringToOString(result, RTL_TEXTENCODING_UTF8));
438 buf.append(RTL_CONSTASCII_STRINGPARAM(" ("));
439 buf.append(static_cast< sal_Int32 >(begin));
440 buf.append(RTL_CONSTASCII_STRINGPARAM(", "));
441 buf.append(static_cast< sal_Int32 >(end));
442 buf.append(')');
443 msg = buf.makeStringAndClear();
444 }
445 CPPUNIT_ASSERT_MESSAGE(msg.getStr(), ok);
446 }
447 }
448
449 css::uno::Reference< css::uno::XComponentContext > Test::m_context;
450
451 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(Test, "alltests");
452
453 }
454
455 NOADDITIONAL;
456