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