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_desktop.hxx" 30 31 #include "dp_registry.hrc" 32 #include "dp_misc.h" 33 #include "dp_resource.h" 34 #include "dp_interact.h" 35 #include "dp_ucb.h" 36 #include "osl/diagnose.h" 37 #include "rtl/ustrbuf.hxx" 38 #include "rtl/uri.hxx" 39 #include "cppuhelper/compbase2.hxx" 40 #include "cppuhelper/exc_hlp.hxx" 41 #include "comphelper/sequence.hxx" 42 #include "ucbhelper/content.hxx" 43 #include "com/sun/star/uno/DeploymentException.hpp" 44 #include "com/sun/star/lang/DisposedException.hpp" 45 #include "com/sun/star/lang/WrappedTargetRuntimeException.hpp" 46 #include "com/sun/star/lang/XServiceInfo.hpp" 47 #include "com/sun/star/lang/XSingleComponentFactory.hpp" 48 #include "com/sun/star/lang/XSingleServiceFactory.hpp" 49 #include "com/sun/star/util/XUpdatable.hpp" 50 #include "com/sun/star/container/XContentEnumerationAccess.hpp" 51 #include "com/sun/star/deployment/PackageRegistryBackend.hpp" 52 #include <hash_map> 53 #include <set> 54 #include <hash_set> 55 #include <memory> 56 57 using namespace ::dp_misc; 58 using namespace ::com::sun::star; 59 using namespace ::com::sun::star::uno; 60 using namespace ::com::sun::star::ucb; 61 using ::rtl::OUString; 62 63 64 namespace dp_registry { 65 66 namespace backend { 67 namespace bundle { 68 Reference<deployment::XPackageRegistry> create( 69 Reference<deployment::XPackageRegistry> const & xRootRegistry, 70 OUString const & context, OUString const & cachePath, bool readOnly, 71 Reference<XComponentContext> const & xComponentContext ); 72 } 73 } 74 75 namespace { 76 77 typedef ::cppu::WeakComponentImplHelper2< 78 deployment::XPackageRegistry, util::XUpdatable > t_helper; 79 80 //============================================================================== 81 class PackageRegistryImpl : private MutexHolder, public t_helper 82 { 83 struct ci_string_hash { 84 ::std::size_t operator () ( OUString const & str ) const { 85 return str.toAsciiLowerCase().hashCode(); 86 } 87 }; 88 struct ci_string_equals { 89 bool operator () ( OUString const & str1, OUString const & str2 ) const{ 90 return str1.equalsIgnoreAsciiCase( str2 ); 91 } 92 }; 93 typedef ::std::hash_map< 94 OUString, Reference<deployment::XPackageRegistry>, 95 ci_string_hash, ci_string_equals > t_string2registry; 96 typedef ::std::hash_map< 97 OUString, OUString, 98 ci_string_hash, ci_string_equals > t_string2string; 99 typedef ::std::set< 100 Reference<deployment::XPackageRegistry> > t_registryset; 101 102 t_string2registry m_mediaType2backend; 103 t_string2string m_filter2mediaType; 104 t_registryset m_ambiguousBackends; 105 t_registryset m_allBackends; 106 ::std::vector< Reference<deployment::XPackageTypeInfo> > m_typesInfos; 107 108 void insertBackend( 109 Reference<deployment::XPackageRegistry> const & xBackend ); 110 111 protected: 112 inline void check(); 113 virtual void SAL_CALL disposing(); 114 115 virtual ~PackageRegistryImpl(); 116 PackageRegistryImpl() : t_helper( getMutex() ) {} 117 118 119 public: 120 static Reference<deployment::XPackageRegistry> create( 121 OUString const & context, 122 OUString const & cachePath, bool readOnly, 123 Reference<XComponentContext> const & xComponentContext ); 124 125 // XUpdatable 126 virtual void SAL_CALL update() throw (RuntimeException); 127 128 // XPackageRegistry 129 virtual Reference<deployment::XPackage> SAL_CALL bindPackage( 130 OUString const & url, OUString const & mediaType, sal_Bool bRemoved, 131 OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) 132 throw (deployment::DeploymentException, 133 deployment::InvalidRemovedParameterException, 134 CommandFailedException, 135 lang::IllegalArgumentException, RuntimeException); 136 virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL 137 getSupportedPackageTypes() throw (RuntimeException); 138 virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) 139 throw (deployment::DeploymentException, 140 RuntimeException); 141 142 }; 143 144 //______________________________________________________________________________ 145 inline void PackageRegistryImpl::check() 146 { 147 ::osl::MutexGuard guard( getMutex() ); 148 if (rBHelper.bInDispose || rBHelper.bDisposed) { 149 throw lang::DisposedException( 150 OUSTR("PackageRegistry instance has already been disposed!"), 151 static_cast<OWeakObject *>(this) ); 152 } 153 } 154 155 //______________________________________________________________________________ 156 void PackageRegistryImpl::disposing() 157 { 158 // dispose all backends: 159 t_registryset::const_iterator iPos( m_allBackends.begin() ); 160 t_registryset::const_iterator const iEnd( m_allBackends.end() ); 161 for ( ; iPos != iEnd; ++iPos ) { 162 try_dispose( *iPos ); 163 } 164 m_mediaType2backend = t_string2registry(); 165 m_ambiguousBackends = t_registryset(); 166 m_allBackends = t_registryset(); 167 168 t_helper::disposing(); 169 } 170 171 //______________________________________________________________________________ 172 PackageRegistryImpl::~PackageRegistryImpl() 173 { 174 } 175 176 //______________________________________________________________________________ 177 OUString normalizeMediaType( OUString const & mediaType ) 178 { 179 ::rtl::OUStringBuffer buf; 180 sal_Int32 index = 0; 181 for (;;) { 182 buf.append( mediaType.getToken( 0, '/', index ).trim() ); 183 if (index < 0) 184 break; 185 buf.append( static_cast< sal_Unicode >('/') ); 186 } 187 return buf.makeStringAndClear(); 188 } 189 190 //______________________________________________________________________________ 191 192 void PackageRegistryImpl::packageRemoved( 193 ::rtl::OUString const & url, ::rtl::OUString const & mediaType) 194 throw (css::deployment::DeploymentException, 195 css::uno::RuntimeException) 196 { 197 const t_string2registry::const_iterator i = 198 m_mediaType2backend.find(mediaType); 199 200 if (i != m_mediaType2backend.end()) 201 { 202 i->second->packageRemoved(url, mediaType); 203 } 204 } 205 206 void PackageRegistryImpl::insertBackend( 207 Reference<deployment::XPackageRegistry> const & xBackend ) 208 { 209 m_allBackends.insert( xBackend ); 210 typedef ::std::hash_set<OUString, ::rtl::OUStringHash> t_stringset; 211 t_stringset ambiguousFilters; 212 213 const Sequence< Reference<deployment::XPackageTypeInfo> > packageTypes( 214 xBackend->getSupportedPackageTypes() ); 215 for ( sal_Int32 pos = 0; pos < packageTypes.getLength(); ++pos ) 216 { 217 Reference<deployment::XPackageTypeInfo> const & xPackageType = 218 packageTypes[ pos ]; 219 m_typesInfos.push_back( xPackageType ); 220 221 const OUString mediaType( normalizeMediaType( 222 xPackageType->getMediaType() ) ); 223 ::std::pair<t_string2registry::iterator, bool> mb_insertion( 224 m_mediaType2backend.insert( t_string2registry::value_type( 225 mediaType, xBackend ) ) ); 226 if (mb_insertion.second) 227 { 228 // add parameterless media-type, too: 229 sal_Int32 semi = mediaType.indexOf( ';' ); 230 if (semi >= 0) { 231 m_mediaType2backend.insert( 232 t_string2registry::value_type( 233 mediaType.copy( 0, semi ), xBackend ) ); 234 } 235 const OUString fileFilter( xPackageType->getFileFilter() ); 236 //The package backend shall also be called to determine the mediatype 237 //(XPackageRegistry.bindPackage) when the URL points to a directory. 238 const bool bExtension = mediaType.equals(OUSTR("application/vnd.sun.star.package-bundle")); 239 if (fileFilter.getLength() == 0 || 240 fileFilter.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("*.*") ) || 241 fileFilter.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("*") ) || 242 bExtension) 243 { 244 m_ambiguousBackends.insert( xBackend ); 245 } 246 else 247 { 248 sal_Int32 nIndex = 0; 249 do { 250 OUString token( fileFilter.getToken( 0, ';', nIndex ) ); 251 if (token.matchAsciiL( RTL_CONSTASCII_STRINGPARAM("*.") )) 252 token = token.copy( 1 ); 253 if (token.getLength() == 0) 254 continue; 255 // mark any further wildcards ambig: 256 bool ambig = (token.indexOf('*') >= 0 || 257 token.indexOf('?') >= 0); 258 if (! ambig) { 259 ::std::pair<t_string2string::iterator, bool> ins( 260 m_filter2mediaType.insert( 261 t_string2string::value_type( 262 token, mediaType ) ) ); 263 ambig = !ins.second; 264 if (ambig) { 265 // filter has already been in: add previously 266 // added backend to ambig set 267 const t_string2registry::const_iterator iFind( 268 m_mediaType2backend.find( 269 /* media-type of pr. added backend */ 270 ins.first->second ) ); 271 OSL_ASSERT( 272 iFind != m_mediaType2backend.end() ); 273 if (iFind != m_mediaType2backend.end()) 274 m_ambiguousBackends.insert( iFind->second ); 275 } 276 } 277 if (ambig) { 278 m_ambiguousBackends.insert( xBackend ); 279 // mark filter to be removed later from filters map: 280 ambiguousFilters.insert( token ); 281 } 282 } 283 while (nIndex >= 0); 284 } 285 } 286 #if OSL_DEBUG_LEVEL > 0 287 else { 288 ::rtl::OUStringBuffer buf; 289 buf.appendAscii( 290 RTL_CONSTASCII_STRINGPARAM( 291 "more than one PackageRegistryBackend for " 292 "media-type=\"") ); 293 buf.append( mediaType ); 294 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\" => ") ); 295 buf.append( Reference<lang::XServiceInfo>( 296 xBackend, UNO_QUERY_THROW )-> 297 getImplementationName() ); 298 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\"!") ); 299 OSL_ENSURE( 0, ::rtl::OUStringToOString( 300 buf.makeStringAndClear(), 301 RTL_TEXTENCODING_UTF8 ) ); 302 } 303 #endif 304 } 305 306 // cut out ambiguous filters: 307 t_stringset::const_iterator iPos( ambiguousFilters.begin() ); 308 const t_stringset::const_iterator iEnd( ambiguousFilters.end() ); 309 for ( ; iPos != iEnd; ++iPos ) { 310 m_filter2mediaType.erase( *iPos ); 311 } 312 } 313 314 //______________________________________________________________________________ 315 Reference<deployment::XPackageRegistry> PackageRegistryImpl::create( 316 OUString const & context, 317 OUString const & cachePath, bool readOnly, 318 Reference<XComponentContext> const & xComponentContext ) 319 { 320 PackageRegistryImpl * that = new PackageRegistryImpl; 321 Reference<deployment::XPackageRegistry> xRet(that); 322 323 // auto-detect all registered package registries: 324 Reference<container::XEnumeration> xEnum( 325 Reference<container::XContentEnumerationAccess>( 326 xComponentContext->getServiceManager(), 327 UNO_QUERY_THROW )->createContentEnumeration( 328 OUSTR("com.sun.star.deployment.PackageRegistryBackend") ) ); 329 if (xEnum.is()) 330 { 331 while (xEnum->hasMoreElements()) 332 { 333 Any element( xEnum->nextElement() ); 334 Sequence<Any> registryArgs( 335 cachePath.getLength() == 0 ? 1 : 3 ); 336 registryArgs[ 0 ] <<= context; 337 if (cachePath.getLength() > 0) 338 { 339 Reference<lang::XServiceInfo> xServiceInfo( 340 element, UNO_QUERY_THROW ); 341 OUString registryCachePath( 342 makeURL( cachePath, 343 ::rtl::Uri::encode( 344 xServiceInfo->getImplementationName(), 345 rtl_UriCharClassPchar, 346 rtl_UriEncodeIgnoreEscapes, 347 RTL_TEXTENCODING_UTF8 ) ) ); 348 registryArgs[ 1 ] <<= registryCachePath; 349 registryArgs[ 2 ] <<= readOnly; 350 if (! readOnly) 351 create_folder( 0, registryCachePath, 352 Reference<XCommandEnvironment>() ); 353 } 354 355 Reference<deployment::XPackageRegistry> xBackend; 356 Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY ); 357 if (xFac.is()) { 358 xBackend.set( 359 xFac->createInstanceWithArgumentsAndContext( 360 registryArgs, xComponentContext ), UNO_QUERY ); 361 } 362 else { 363 Reference<lang::XSingleServiceFactory> xSingleServiceFac( 364 element, UNO_QUERY_THROW ); 365 xBackend.set( 366 xSingleServiceFac->createInstanceWithArguments( 367 registryArgs ), UNO_QUERY ); 368 } 369 if (! xBackend.is()) { 370 throw DeploymentException( 371 OUSTR("cannot instantiate PackageRegistryBackend service: ") 372 + Reference<lang::XServiceInfo>( 373 element, UNO_QUERY_THROW )->getImplementationName(), 374 static_cast<OWeakObject *>(that) ); 375 } 376 377 that->insertBackend( xBackend ); 378 } 379 } 380 381 // Insert bundle back-end. 382 // Always register as last, because we want to add extensions also as folders 383 // and as a default we accept every folder, which was not recognized by the other 384 // backends. 385 Reference<deployment::XPackageRegistry> extensionBackend = 386 ::dp_registry::backend::bundle::create( 387 that, context, cachePath, readOnly, xComponentContext); 388 that->insertBackend(extensionBackend); 389 390 Reference<lang::XServiceInfo> xServiceInfo( 391 extensionBackend, UNO_QUERY_THROW ); 392 393 OSL_ASSERT(xServiceInfo.is()); 394 OUString registryCachePath( 395 makeURL( cachePath, 396 ::rtl::Uri::encode( 397 xServiceInfo->getImplementationName(), 398 rtl_UriCharClassPchar, 399 rtl_UriEncodeIgnoreEscapes, 400 RTL_TEXTENCODING_UTF8 ) ) ); 401 create_folder( 0, registryCachePath, Reference<XCommandEnvironment>()); 402 403 404 #if OSL_DEBUG_LEVEL > 1 405 // dump tables: 406 { 407 t_registryset allBackends; 408 dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" ); 409 for ( t_string2string::const_iterator iPos( 410 that->m_filter2mediaType.begin() ); 411 iPos != that->m_filter2mediaType.end(); ++iPos ) 412 { 413 ::rtl::OUStringBuffer buf; 414 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("extension \"") ); 415 buf.append( iPos->first ); 416 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( 417 "\" maps to media-type \"") ); 418 buf.append( iPos->second ); 419 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( 420 "\" maps to backend ") ); 421 const Reference<deployment::XPackageRegistry> xBackend( 422 that->m_mediaType2backend.find( iPos->second )->second ); 423 allBackends.insert( xBackend ); 424 buf.append( Reference<lang::XServiceInfo>( 425 xBackend, UNO_QUERY_THROW ) 426 ->getImplementationName() ); 427 dp_misc::writeConsole( buf.makeStringAndClear() + OUSTR("\n")); 428 } 429 dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" ); 430 for ( t_registryset::const_iterator iPos( 431 that->m_ambiguousBackends.begin() ); 432 iPos != that->m_ambiguousBackends.end(); ++iPos ) 433 { 434 ::rtl::OUStringBuffer buf; 435 buf.append( 436 Reference<lang::XServiceInfo>( 437 *iPos, UNO_QUERY_THROW )->getImplementationName() ); 438 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(": ") ); 439 const Sequence< Reference<deployment::XPackageTypeInfo> > types( 440 (*iPos)->getSupportedPackageTypes() ); 441 for ( sal_Int32 pos = 0; pos < types.getLength(); ++pos ) { 442 Reference<deployment::XPackageTypeInfo> const & xInfo = 443 types[ pos ]; 444 buf.append( xInfo->getMediaType() ); 445 const OUString filter( xInfo->getFileFilter() ); 446 if (filter.getLength() > 0) { 447 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(" (") ); 448 buf.append( filter ); 449 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(")") ); 450 } 451 if (pos < (types.getLength() - 1)) 452 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(", ") ); 453 } 454 dp_misc::TRACE(buf.makeStringAndClear() + OUSTR("\n\n")); 455 } 456 allBackends.insert( that->m_ambiguousBackends.begin(), 457 that->m_ambiguousBackends.end() ); 458 OSL_ASSERT( allBackends == that->m_allBackends ); 459 } 460 #endif 461 462 return xRet; 463 } 464 465 // XUpdatable: broadcast to backends 466 //______________________________________________________________________________ 467 void PackageRegistryImpl::update() throw (RuntimeException) 468 { 469 check(); 470 t_registryset::const_iterator iPos( m_allBackends.begin() ); 471 const t_registryset::const_iterator iEnd( m_allBackends.end() ); 472 for ( ; iPos != iEnd; ++iPos ) { 473 const Reference<util::XUpdatable> xUpdatable( *iPos, UNO_QUERY ); 474 if (xUpdatable.is()) 475 xUpdatable->update(); 476 } 477 } 478 479 // XPackageRegistry 480 //______________________________________________________________________________ 481 Reference<deployment::XPackage> PackageRegistryImpl::bindPackage( 482 OUString const & url, OUString const & mediaType_, sal_Bool bRemoved, 483 OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) 484 throw (deployment::DeploymentException, deployment::InvalidRemovedParameterException, 485 CommandFailedException, 486 lang::IllegalArgumentException, RuntimeException) 487 { 488 check(); 489 OUString mediaType(mediaType_); 490 if (mediaType.getLength() == 0) 491 { 492 ::ucbhelper::Content ucbContent; 493 if (create_ucb_content( 494 &ucbContent, url, xCmdEnv, false /* no throw */ ) 495 && !ucbContent.isFolder()) 496 { 497 OUString title( ucbContent.getPropertyValue( 498 StrTitle::get() ).get<OUString>() ); 499 for (;;) 500 { 501 const t_string2string::const_iterator iFind( 502 m_filter2mediaType.find(title) ); 503 if (iFind != m_filter2mediaType.end()) { 504 mediaType = iFind->second; 505 break; 506 } 507 sal_Int32 point = title.indexOf( '.', 1 /* consume . */ ); 508 if (point < 0) 509 break; 510 title = title.copy(point); 511 } 512 } 513 } 514 if (mediaType.getLength() == 0) 515 { 516 // try ambiguous backends: 517 t_registryset::const_iterator iPos( m_ambiguousBackends.begin() ); 518 const t_registryset::const_iterator iEnd( m_ambiguousBackends.end() ); 519 for ( ; iPos != iEnd; ++iPos ) 520 { 521 try { 522 return (*iPos)->bindPackage( url, mediaType, bRemoved, 523 identifier, xCmdEnv ); 524 } 525 catch (lang::IllegalArgumentException &) { 526 } 527 } 528 throw lang::IllegalArgumentException( 529 getResourceString(RID_STR_CANNOT_DETECT_MEDIA_TYPE) + url, 530 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) ); 531 } 532 else 533 { 534 // get backend by media-type: 535 t_string2registry::const_iterator iFind( 536 m_mediaType2backend.find( normalizeMediaType(mediaType) ) ); 537 if (iFind == m_mediaType2backend.end()) { 538 // xxx todo: more sophisticated media-type argument parsing... 539 sal_Int32 q = mediaType.indexOf( ';' ); 540 if (q >= 0) { 541 iFind = m_mediaType2backend.find( 542 normalizeMediaType( 543 // cut parameters: 544 mediaType.copy( 0, q ) ) ); 545 } 546 } 547 if (iFind == m_mediaType2backend.end()) { 548 throw lang::IllegalArgumentException( 549 getResourceString(RID_STR_UNSUPPORTED_MEDIA_TYPE) + mediaType, 550 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) ); 551 } 552 return iFind->second->bindPackage( url, mediaType, bRemoved, 553 identifier, xCmdEnv ); 554 } 555 } 556 557 //______________________________________________________________________________ 558 Sequence< Reference<deployment::XPackageTypeInfo> > 559 PackageRegistryImpl::getSupportedPackageTypes() throw (RuntimeException) 560 { 561 return comphelper::containerToSequence(m_typesInfos); 562 } 563 } // anon namespace 564 565 //============================================================================== 566 Reference<deployment::XPackageRegistry> SAL_CALL create( 567 OUString const & context, 568 OUString const & cachePath, bool readOnly, 569 Reference<XComponentContext> const & xComponentContext ) 570 { 571 return PackageRegistryImpl::create( 572 context, cachePath, readOnly, xComponentContext ); 573 } 574 575 } // namespace dp_registry 576 577