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