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 91 getIdentifier() throw (css::uno::RuntimeException) { 92 return m_identifier; 93 } 94 95 virtual rtl::OUString SAL_CALL getContentType() 96 throw (css::uno::RuntimeException) 97 { 98 return rtl::OUString(); 99 } 100 101 virtual void SAL_CALL addContentEventListener( 102 css::uno::Reference< css::ucb::XContentEventListener > const &) 103 throw (css::uno::RuntimeException) 104 {} 105 106 virtual void SAL_CALL removeContentEventListener( 107 css::uno::Reference< css::ucb::XContentEventListener > const &) 108 throw (css::uno::RuntimeException) 109 {} 110 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 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 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 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: 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 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 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 237 void Test::finish() { 238 css::uno::Reference< css::lang::XComponent >( 239 m_context, css::uno::UNO_QUERY_THROW)->dispose(); 240 } 241 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 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