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