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 94 getIdentifier() throw (css::uno::RuntimeException) { 95 return m_identifier; 96 } 97 98 virtual rtl::OUString SAL_CALL getContentType() 99 throw (css::uno::RuntimeException) 100 { 101 return rtl::OUString(); 102 } 103 104 virtual void SAL_CALL addContentEventListener( 105 css::uno::Reference< css::ucb::XContentEventListener > const &) 106 throw (css::uno::RuntimeException) 107 {} 108 109 virtual void SAL_CALL removeContentEventListener( 110 css::uno::Reference< css::ucb::XContentEventListener > const &) 111 throw (css::uno::RuntimeException) 112 {} 113 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 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 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 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: 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 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 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 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 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 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 451 int main(int argc, char **argv) 452 { 453 ::testing::InitGoogleTest(&argc, argv); 454 return RUN_ALL_TESTS(); 455 } 456