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_package.hrc" 28 #include "dp_backend.h" 29 #include "dp_ucb.h" 30 #include "dp_interact.h" 31 #include "dp_dependencies.hxx" 32 #include "dp_platform.hxx" 33 #include "dp_descriptioninfoset.hxx" 34 #include "dp_identifier.hxx" 35 #include "rtl/uri.hxx" 36 #include "cppuhelper/exc_hlp.hxx" 37 #include "cppuhelper/implbase1.hxx" 38 #include "ucbhelper/content.hxx" 39 #include "svl/inettype.hxx" 40 #include "comphelper/anytostring.hxx" 41 #include "comphelper/makesequence.hxx" 42 #include "comphelper/sequence.hxx" 43 #include "com/sun/star/lang/WrappedTargetException.hpp" 44 #include "com/sun/star/lang/XServiceInfo.hpp" 45 #include "com/sun/star/beans/UnknownPropertyException.hpp" 46 #include "com/sun/star/graphic/XGraphic.hpp" 47 #include "com/sun/star/graphic/XGraphicProvider.hpp" 48 #include "com/sun/star/io/XOutputStream.hpp" 49 #include "com/sun/star/io/XInputStream.hpp" 50 #include "com/sun/star/task/InteractionClassification.hpp" 51 #include "com/sun/star/task/XInteractionApprove.hpp" 52 #include "com/sun/star/ucb/XInteractionReplaceExistingData.hpp" 53 #include "com/sun/star/ucb/NameClashResolveRequest.hpp" 54 #include "com/sun/star/ucb/XContentAccess.hpp" 55 #include "com/sun/star/ucb/NameClash.hpp" 56 #include "com/sun/star/ucb/UnsupportedCommandException.hpp" 57 #include "com/sun/star/sdbc/XResultSet.hpp" 58 #include "com/sun/star/sdbc/XRow.hpp" 59 #include "com/sun/star/packages/manifest/XManifestReader.hpp" 60 #include "com/sun/star/packages/manifest/XManifestWriter.hpp" 61 #include "com/sun/star/deployment/DependencyException.hpp" 62 #include "com/sun/star/deployment/LicenseException.hpp" 63 #include "com/sun/star/deployment/PlatformException.hpp" 64 #include "com/sun/star/deployment/Prerequisites.hpp" 65 #include "com/sun/star/xml/dom/XDocumentBuilder.hpp" 66 #include "com/sun/star/xml/xpath/XXPathAPI.hpp" 67 #include "com/sun/star/deployment/XPackageManager.hpp" 68 #include "boost/optional.hpp" 69 #include <vector> 70 #include <stdio.h> 71 72 #include "dp_extbackenddb.hxx" 73 using namespace ::dp_misc; 74 using namespace ::com::sun::star; 75 using namespace ::com::sun::star::uno; 76 77 namespace css = ::com::sun::star; 78 79 using ::rtl::OUString; 80 81 namespace dp_registry { 82 namespace backend { 83 namespace bundle { 84 namespace { 85 86 typedef cppu::ImplInheritanceHelper1<PackageRegistryBackend, 87 lang::XServiceInfo> ImplBaseT; 88 89 //============================================================================== 90 class BackendImpl : public ImplBaseT 91 { 92 class PackageImpl : public ::dp_registry::backend::Package 93 { 94 BackendImpl * getMyBackend() const; 95 /** constains the old tooltip description for the Extension Manager GUI in OOo v.2.x 96 We keep it for backward compatibility. 97 */ 98 OUString m_oldDescription; 99 OUString m_url_expanded; 100 const bool m_legacyBundle; 101 Sequence< Reference<deployment::XPackage> > m_bundle; 102 Sequence< Reference<deployment::XPackage> > * m_pBundle; 103 104 ExtensionBackendDb::Data m_dbData; 105 106 Reference<deployment::XPackage> bindBundleItem( 107 OUString const & url, OUString const & mediaType, 108 sal_Bool bRemoved, //that is, useing data base information 109 OUString const & identifier, 110 Reference<ucb::XCommandEnvironment> const & xCmdEnv, 111 bool notifyDetectionError = true ); 112 113 typedef ::std::vector< Reference<deployment::XPackage> > t_packagevec; 114 void scanBundle( 115 t_packagevec & bundle, 116 ::rtl::Reference<AbortChannel> const & abortChannel, 117 Reference<ucb::XCommandEnvironment> const & xCmdEnv ); 118 void scanLegacyBundle( 119 t_packagevec & bundle, 120 OUString const & url, 121 ::rtl::Reference<AbortChannel> const & abortChannel, 122 Reference<ucb::XCommandEnvironment> const & xCmdEnv, 123 bool skip_registration = false ); 124 ::std::vector<Reference<deployment::XPackage> > getPackagesFromDb( 125 Reference<ucb::XCommandEnvironment> const & xCmdEnv); 126 bool checkPlatform( 127 Reference<ucb::XCommandEnvironment > const & environment); 128 129 bool checkDependencies( 130 Reference<ucb::XCommandEnvironment > const & 131 environment, 132 DescriptionInfoset const & description); 133 // throws css::uno::RuntimeException, 134 // css::deployment::DeploymentException 135 136 ::sal_Bool checkLicense( 137 Reference< ucb::XCommandEnvironment > const & xCmdEnv, 138 DescriptionInfoset const & description, bool bNoLicenseChecking) 139 throw (deployment::DeploymentException, 140 ucb::CommandFailedException, 141 ucb::CommandAbortedException, 142 RuntimeException); 143 // @throws DeploymentException 144 OUString getTextFromURL( 145 const Reference< ucb::XCommandEnvironment >& xCmdEnv, 146 const OUString& licenseUrl); 147 148 DescriptionInfoset getDescriptionInfoset(); 149 150 // Package 151 virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_( 152 ::osl::ResettableMutexGuard & guard, 153 ::rtl::Reference<AbortChannel> const & abortChannel, 154 Reference<ucb::XCommandEnvironment> const & xCmdEnv ); 155 virtual void processPackage_( 156 ::osl::ResettableMutexGuard & guard, 157 bool registerPackage, 158 bool startup, 159 ::rtl::Reference<AbortChannel> const & abortChannel, 160 Reference<ucb::XCommandEnvironment> const & xCmdEnv ); 161 162 virtual void SAL_CALL disposing(); 163 164 165 166 public: 167 PackageImpl( 168 ::rtl::Reference<PackageRegistryBackend> const & myBackend, 169 OUString const & url, 170 OUString const & name, 171 Reference<deployment::XPackageTypeInfo> const & xPackageType, 172 bool legacyBundle, 173 bool bRemoved, 174 OUString const & identifier); 175 176 // XPackage 177 virtual sal_Bool SAL_CALL isBundle() throw (RuntimeException); 178 179 virtual Sequence< Reference<deployment::XPackage> > SAL_CALL getBundle( 180 Reference<task::XAbortChannel> const & xAbortChannel, 181 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) 182 throw (deployment::DeploymentException, 183 ucb::CommandFailedException, 184 ucb::CommandAbortedException, 185 lang::IllegalArgumentException, RuntimeException); 186 virtual OUString SAL_CALL getDescription() 187 throw (deployment::ExtensionRemovedException, RuntimeException); 188 189 virtual OUString SAL_CALL getLicenseText() 190 throw (deployment::ExtensionRemovedException, RuntimeException); 191 192 virtual void SAL_CALL exportTo( 193 OUString const & destFolderURL, OUString const & newTitle, 194 sal_Int32 nameClashAction, 195 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) 196 throw (deployment::ExtensionRemovedException, 197 ucb::CommandFailedException, 198 ucb::CommandAbortedException, 199 RuntimeException); 200 201 virtual ::sal_Int32 SAL_CALL checkPrerequisites( 202 const Reference< task::XAbortChannel >& xAbortChannel, 203 const Reference< ucb::XCommandEnvironment >& xCmdEnv, 204 ::sal_Bool noLicenseChecking) 205 throw (deployment::ExtensionRemovedException, 206 deployment::DeploymentException, 207 ucb::CommandFailedException, 208 ucb::CommandAbortedException, 209 RuntimeException); 210 211 virtual ::sal_Bool SAL_CALL checkDependencies( 212 const Reference< ucb::XCommandEnvironment >& xCmdEnv ) 213 throw (deployment::DeploymentException, 214 deployment::ExtensionRemovedException, 215 ucb::CommandFailedException, 216 RuntimeException); 217 218 virtual beans::Optional<OUString> SAL_CALL getIdentifier() 219 throw (RuntimeException); 220 221 virtual OUString SAL_CALL getVersion() 222 throw (deployment::ExtensionRemovedException, RuntimeException); 223 224 virtual Sequence<OUString> SAL_CALL getUpdateInformationURLs() 225 throw (deployment::ExtensionRemovedException, RuntimeException); 226 227 virtual beans::StringPair SAL_CALL getPublisherInfo() 228 throw (deployment::ExtensionRemovedException, RuntimeException); 229 230 virtual OUString SAL_CALL getDisplayName() 231 throw (deployment::ExtensionRemovedException, RuntimeException); 232 233 virtual Reference< graphic::XGraphic > SAL_CALL 234 getIcon( ::sal_Bool bHighContrast ) 235 throw (deployment::ExtensionRemovedException, 236 RuntimeException); 237 }; 238 friend class PackageImpl; 239 240 Reference<deployment::XPackageRegistry> m_xRootRegistry; 241 const Reference<deployment::XPackageTypeInfo> m_xBundleTypeInfo; 242 const Reference<deployment::XPackageTypeInfo> m_xLegacyBundleTypeInfo; 243 Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos; 244 245 std::auto_ptr<ExtensionBackendDb> m_backendDb; 246 247 void addDataToDb(OUString const & url, ExtensionBackendDb::Data const & data); 248 ExtensionBackendDb::Data readDataFromDb(OUString const & url); 249 void revokeEntryFromDb(OUString const & url); 250 251 // PackageRegistryBackend 252 virtual Reference<deployment::XPackage> bindPackage_( 253 OUString const & url, OUString const & mediaType, 254 sal_Bool bRemoved, OUString const & identifier, 255 Reference<ucb::XCommandEnvironment> const & xCmdEnv ); 256 257 virtual void SAL_CALL disposing(); 258 259 public: 260 BackendImpl( 261 Sequence<Any> const & args, 262 Reference<XComponentContext> const & xComponentContext, 263 Reference<deployment::XPackageRegistry> const & xRootRegistry ); 264 265 // XServiceInfo 266 virtual OUString SAL_CALL getImplementationName() throw (RuntimeException); 267 virtual sal_Bool SAL_CALL supportsService( OUString const& name ) 268 throw (RuntimeException); 269 virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() 270 throw (RuntimeException); 271 272 // XPackageRegistry 273 virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL 274 getSupportedPackageTypes() throw (RuntimeException); 275 virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) 276 throw (deployment::DeploymentException, 277 uno::RuntimeException); 278 279 using ImplBaseT::disposing; 280 }; 281 282 //Used to find a XPackage with a particular URL 283 class XPackage_eq : public std::unary_function<Reference<deployment::XPackage>, bool> 284 { 285 OUString m_URL; 286 public: 287 explicit XPackage_eq(const OUString & s) : m_URL(s) {} 288 bool operator() (const Reference<deployment::XPackage> & p) const 289 { 290 return m_URL.equals(p->getURL()); 291 } 292 }; 293 294 //______________________________________________________________________________ 295 BackendImpl::BackendImpl( 296 Sequence<Any> const & args, 297 Reference<XComponentContext> const & xComponentContext, 298 Reference<deployment::XPackageRegistry> const & xRootRegistry ) 299 : ImplBaseT( args, xComponentContext ), 300 m_xRootRegistry( xRootRegistry ), 301 m_xBundleTypeInfo( new Package::TypeInfo( 302 OUSTR("application/vnd.sun.star.package-bundle"), 303 OUSTR("*.oxt;*.uno.pkg"), 304 getResourceString(RID_STR_PACKAGE_BUNDLE), 305 RID_IMG_DEF_PACKAGE_BUNDLE, 306 RID_IMG_DEF_PACKAGE_BUNDLE_HC ) ), 307 m_xLegacyBundleTypeInfo( new Package::TypeInfo( 308 OUSTR("application/" 309 "vnd.sun.star.legacy-package-bundle"), 310 OUSTR("*.zip"), 311 m_xBundleTypeInfo->getShortDescription(), 312 RID_IMG_DEF_PACKAGE_BUNDLE, 313 RID_IMG_DEF_PACKAGE_BUNDLE_HC ) ), 314 m_typeInfos(2) 315 { 316 m_typeInfos[ 0 ] = m_xBundleTypeInfo; 317 m_typeInfos[ 1 ] = m_xLegacyBundleTypeInfo; 318 319 if (!transientMode()) 320 { 321 OUString dbFile = makeURL(getCachePath(), getImplementationName()); 322 dbFile = makeURL(dbFile, OUSTR("backenddb.xml")); 323 m_backendDb.reset( 324 new ExtensionBackendDb(getComponentContext(), dbFile)); 325 } 326 } 327 328 //______________________________________________________________________________ 329 void BackendImpl::disposing() 330 { 331 m_xRootRegistry.clear(); 332 PackageRegistryBackend::disposing(); 333 } 334 335 // XServiceInfo 336 OUString BackendImpl::getImplementationName() throw (RuntimeException) 337 { 338 return OUSTR("com.sun.star.comp.deployment.bundle.PackageRegistryBackend"); 339 } 340 341 sal_Bool BackendImpl::supportsService( OUString const& name ) 342 throw (RuntimeException) 343 { 344 return getSupportedServiceNames()[0].equals(name); 345 } 346 347 Sequence<OUString> BackendImpl::getSupportedServiceNames() 348 throw (RuntimeException) 349 { 350 return comphelper::makeSequence( 351 OUString::createFromAscii(BACKEND_SERVICE_NAME) ); 352 } 353 354 // XPackageRegistry 355 //______________________________________________________________________________ 356 Sequence< Reference<deployment::XPackageTypeInfo> > 357 BackendImpl::getSupportedPackageTypes() throw (RuntimeException) 358 { 359 return m_typeInfos; 360 } 361 362 void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/) 363 throw (deployment::DeploymentException, 364 uno::RuntimeException) 365 { 366 //Notify the backend responsible for processing the different media 367 //types that this extension was removed. 368 ExtensionBackendDb::Data data = readDataFromDb(url); 369 for (ExtensionBackendDb::Data::ITC_ITEMS i = data.items.begin(); i != data.items.end(); i++) 370 { 371 m_xRootRegistry->packageRemoved(i->first, i->second); 372 } 373 374 if (m_backendDb.get()) 375 m_backendDb->removeEntry(url); 376 } 377 378 379 // PackageRegistryBackend 380 //______________________________________________________________________________ 381 Reference<deployment::XPackage> BackendImpl::bindPackage_( 382 OUString const & url, OUString const & mediaType_, 383 sal_Bool bRemoved, OUString const & identifier, 384 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) 385 { 386 OUString mediaType( mediaType_ ); 387 if (mediaType.getLength() == 0) 388 { 389 // detect media-type: 390 ::ucbhelper::Content ucbContent; 391 if (create_ucb_content( &ucbContent, url, xCmdEnv )) 392 { 393 if (ucbContent.isFolder()) 394 { 395 //Every .oxt, uno.pkg file must contain a META-INF folder 396 ::ucbhelper::Content metaInfContent; 397 if (create_ucb_content( 398 &metaInfContent, makeURL( url, OUSTR("META-INF") ), 399 xCmdEnv, false /* no throw */ )) 400 { 401 mediaType = OUSTR("application/vnd.sun.star.package-bundle"); 402 } 403 //No support of legacy bundles, because every folder could be one. 404 } 405 else 406 { 407 const OUString title( ucbContent.getPropertyValue( 408 StrTitle::get() ).get<OUString>() ); 409 if (title.endsWithIgnoreAsciiCaseAsciiL( 410 RTL_CONSTASCII_STRINGPARAM(".oxt") ) || 411 title.endsWithIgnoreAsciiCaseAsciiL( 412 RTL_CONSTASCII_STRINGPARAM(".uno.pkg") )) 413 mediaType = OUSTR("application/vnd.sun.star.package-bundle"); 414 else if (title.endsWithIgnoreAsciiCaseAsciiL( 415 RTL_CONSTASCII_STRINGPARAM(".zip") )) 416 mediaType = 417 OUSTR("application/vnd.sun.star.legacy-package-bundle"); 418 } 419 } 420 if (mediaType.getLength() == 0) 421 throw lang::IllegalArgumentException( 422 StrCannotDetectMediaType::get() + url, 423 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) ); 424 } 425 426 String type, subType; 427 INetContentTypeParameterList params; 428 if (INetContentTypes::parse( mediaType, type, subType, ¶ms )) 429 { 430 if (type.EqualsIgnoreCaseAscii("application")) 431 { 432 433 //In case a XPackage is created for a removed extension, we cannot 434 //obtain the name 435 OUString name; 436 if (!bRemoved) 437 { 438 ::ucbhelper::Content ucbContent( url, xCmdEnv ); 439 name = ucbContent.getPropertyValue( 440 StrTitle::get() ).get<OUString>(); 441 } 442 if (subType.EqualsIgnoreCaseAscii("vnd.sun.star.package-bundle")) { 443 return new PackageImpl( 444 this, url, name, m_xBundleTypeInfo, false, bRemoved, 445 identifier); 446 } 447 else if (subType.EqualsIgnoreCaseAscii( 448 "vnd.sun.star.legacy-package-bundle")) { 449 return new PackageImpl( 450 this, url, name, m_xLegacyBundleTypeInfo, true, bRemoved, 451 identifier); 452 } 453 } 454 } 455 throw lang::IllegalArgumentException( 456 StrUnsupportedMediaType::get() + mediaType, 457 static_cast<OWeakObject *>(this), 458 static_cast<sal_Int16>(-1) ); 459 } 460 461 void BackendImpl::addDataToDb( 462 OUString const & url, ExtensionBackendDb::Data const & data) 463 { 464 if (m_backendDb.get()) 465 m_backendDb->addEntry(url, data); 466 } 467 468 ExtensionBackendDb::Data BackendImpl::readDataFromDb( 469 OUString const & url) 470 { 471 ExtensionBackendDb::Data data; 472 if (m_backendDb.get()) 473 data = m_backendDb->getEntry(url); 474 return data; 475 } 476 477 void BackendImpl::revokeEntryFromDb(OUString const & url) 478 { 479 if (m_backendDb.get()) 480 m_backendDb->revokeEntry(url); 481 } 482 483 484 //############################################################################## 485 486 BackendImpl::PackageImpl::PackageImpl( 487 ::rtl::Reference<PackageRegistryBackend> const & myBackend, 488 OUString const & url, 489 OUString const & name, 490 Reference<deployment::XPackageTypeInfo> const & xPackageType, 491 bool legacyBundle, bool bRemoved, OUString const & identifier) 492 : Package( myBackend, url, name, name /* display-name */, 493 xPackageType, bRemoved, identifier), 494 m_url_expanded( expandUnoRcUrl( url ) ), 495 m_legacyBundle( legacyBundle ), 496 m_pBundle( 0 ) 497 { 498 if (bRemoved) 499 m_dbData = getMyBackend()->readDataFromDb(url); 500 } 501 502 BackendImpl * BackendImpl::PackageImpl::getMyBackend() const 503 { 504 BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get()); 505 if (NULL == pBackend) 506 { 507 //May throw a DisposedException 508 check(); 509 //We should never get here... 510 throw RuntimeException( 511 OUSTR("Failed to get the BackendImpl"), 512 static_cast<OWeakObject*>(const_cast<PackageImpl *>(this))); 513 } 514 return pBackend; 515 } 516 //______________________________________________________________________________ 517 void BackendImpl::PackageImpl::disposing() 518 { 519 sal_Int32 len = m_bundle.getLength(); 520 Reference<deployment::XPackage> const * p = m_bundle.getConstArray(); 521 for ( sal_Int32 pos = 0; pos < len; ++pos ) 522 try_dispose( p[ pos ] ); 523 m_bundle.realloc( 0 ); 524 525 Package::disposing(); 526 } 527 528 // Package 529 //______________________________________________________________________________ 530 beans::Optional< beans::Ambiguous<sal_Bool> > 531 BackendImpl::PackageImpl::isRegistered_( 532 ::osl::ResettableMutexGuard &, 533 ::rtl::Reference<AbortChannel> const & abortChannel, 534 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) 535 { 536 //In case the object was created for a removed extension (m_bRemoved = true) 537 //but the extension is not registered, then bundle will be empty. Then 538 //the return value will be Optional<...>.IsPresent= false. Althoug this is 539 //not true, this does not matter. Then registerPackage or revokePackage 540 //would never be called for the items. But since the extension is removed 541 //and not registered anyway, this does not matter. 542 const Sequence< Reference<deployment::XPackage> > bundle( 543 getBundle( abortChannel.get(), xCmdEnv ) ); 544 545 bool reg = false; 546 bool present = false; 547 bool ambig = false; 548 for ( sal_Int32 pos = bundle.getLength(); pos--; ) 549 { 550 Reference<deployment::XPackage> const & xPackage = bundle[ pos ]; 551 Reference<task::XAbortChannel> xSubAbortChannel( 552 xPackage->createAbortChannel() ); 553 AbortChannel::Chain chain( abortChannel, xSubAbortChannel ); 554 beans::Optional< beans::Ambiguous<sal_Bool> > option( 555 xPackage->isRegistered( xSubAbortChannel, xCmdEnv ) ); 556 557 //present = true if at least one bundle item has this value. 558 //reg = true if all bundle items have an option value (option.IsPresent == 1) 559 //and all have value of true (option.Value.Value == true) 560 //If not, then the bundle has the status of not registered and ambiguous. 561 if (option.IsPresent) 562 { 563 beans::Ambiguous<sal_Bool> const & status = option.Value; 564 if (present) 565 { 566 //we never come here in the first iteration 567 if (reg != (status.Value != sal_False)) { 568 569 ambig = true; 570 reg = false; 571 break; 572 } 573 } 574 else 575 { 576 //we always come here in the first iteration 577 reg = status.Value; 578 present = true; 579 } 580 } 581 } 582 return beans::Optional< beans::Ambiguous<sal_Bool> >( 583 present, beans::Ambiguous<sal_Bool>(reg, ambig) ); 584 } 585 586 OUString BackendImpl::PackageImpl::getTextFromURL( 587 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv, 588 const OUString& licenseUrl) 589 { 590 try 591 { 592 ::ucbhelper::Content descContent(licenseUrl, xCmdEnv); 593 ::rtl::ByteSequence seq = dp_misc::readFile(descContent); 594 return OUString( reinterpret_cast<sal_Char const *>( 595 seq.getConstArray()), seq.getLength(), RTL_TEXTENCODING_UTF8); 596 } 597 catch (css::uno::Exception&) 598 { 599 Any exc( ::cppu::getCaughtException() ); 600 throw css::deployment::DeploymentException( 601 OUSTR("Could not read file ") + licenseUrl, 0, exc); 602 } 603 604 } 605 606 DescriptionInfoset BackendImpl::PackageImpl::getDescriptionInfoset() 607 { 608 return dp_misc::getDescriptionInfoset(m_url_expanded); 609 } 610 611 bool BackendImpl::PackageImpl::checkPlatform( 612 css::uno::Reference< css::ucb::XCommandEnvironment > const & environment) 613 { 614 bool ret = false; 615 DescriptionInfoset info(getDescriptionInfoset()); 616 Sequence<OUString> platforms(info.getSupportedPlaforms()); 617 if (hasValidPlatform(platforms)) 618 { 619 ret = true; 620 } 621 else 622 { 623 ret = false; 624 rtl::OUString msg( 625 RTL_CONSTASCII_USTRINGPARAM("unsupported platform")); 626 Any e( 627 css::deployment::PlatformException( 628 msg, static_cast<OWeakObject *>(this), this)); 629 if (!interactContinuation( 630 e, cppu::UnoType< css::task::XInteractionApprove >::get(), 631 environment, NULL, NULL)) 632 { 633 throw css::deployment::DeploymentException( 634 msg, static_cast<OWeakObject *>(this), e); 635 } 636 } 637 return ret; 638 } 639 640 641 bool BackendImpl::PackageImpl::checkDependencies( 642 css::uno::Reference< css::ucb::XCommandEnvironment > const & environment, 643 DescriptionInfoset const & description) 644 { 645 css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > > 646 unsatisfied(dp_misc::Dependencies::check(description)); 647 648 if (unsatisfied.getLength() == 0) { 649 return true; 650 } else { 651 rtl::OUString msg( 652 RTL_CONSTASCII_USTRINGPARAM("unsatisfied dependencies")); 653 Any e( 654 css::deployment::DependencyException( 655 msg, static_cast<OWeakObject *>(this), unsatisfied)); 656 if (!interactContinuation( 657 e, cppu::UnoType< css::task::XInteractionApprove >::get(), 658 environment, NULL, NULL)) 659 { 660 throw css::deployment::DeploymentException( 661 msg, static_cast<OWeakObject *>(this), e); 662 } 663 return false; 664 } 665 } 666 667 ::sal_Bool BackendImpl::PackageImpl::checkLicense( 668 css::uno::Reference< css::ucb::XCommandEnvironment > const & xCmdEnv, 669 DescriptionInfoset const & info, bool alreadyInstalled) 670 throw (css::deployment::DeploymentException, 671 css::ucb::CommandFailedException, 672 css::ucb::CommandAbortedException, 673 css::uno::RuntimeException) 674 { 675 try 676 { 677 ::boost::optional<SimpleLicenseAttributes> simplLicAttr 678 = info.getSimpleLicenseAttributes(); 679 if (! simplLicAttr) 680 return true; 681 OUString sLic = info.getLocalizedLicenseURL(); 682 //If we do not get a localized licence then there is an error in the description.xml 683 //This should be handled by using a validating parser. Therefore we assume that no 684 //license is available. 685 if (sLic.getLength() == 0) 686 throw css::deployment::DeploymentException( 687 OUSTR("Could not obtain path to license. Possible error in description.xml"), 0, Any()); 688 OUString sHref = m_url_expanded + OUSTR("/") + sLic; 689 OUString sLicense = getTextFromURL(xCmdEnv, sHref); 690 ////determine who has to agree to the license 691 //check correct value for attribute 692 if ( ! (simplLicAttr->acceptBy.equals(OUSTR("user")) || simplLicAttr->acceptBy.equals(OUSTR("admin")))) 693 throw css::deployment::DeploymentException( 694 OUSTR("Could not obtain attribute simple-lincense@accept-by or it has no valid value"), 0, Any()); 695 696 697 //Only use interaction if there is no version of this extension already installed 698 //and the suppress-on-update flag is not set for the new extension 699 // alreadyInstalled | bSuppressOnUpdate | show license 700 //---------------------------------------- 701 // 0 | 0 | 1 702 // 0 | 1 | 1 703 // 1 | 0 | 1 704 // 1 | 1 | 0 705 706 if ( !(alreadyInstalled && simplLicAttr->suppressOnUpdate)) 707 { 708 css::deployment::LicenseException licExc( 709 OUString(), 0, getDisplayName(), sLicense, 710 simplLicAttr->acceptBy); 711 bool approve = false; 712 bool abort = false; 713 if (! interactContinuation( 714 Any(licExc), task::XInteractionApprove::static_type(), xCmdEnv, &approve, &abort )) 715 throw css::deployment::DeploymentException( 716 OUSTR("Could not interact with user."), 0, Any()); 717 718 if (approve == true) 719 return true; 720 else 721 return false; 722 //throw css::deployment::DeploymentException( 723 // OUSTR("Extension Manager: User declined the license."), 724 // static_cast<OWeakObject*>(this), 725 // Any( css::deployment::LicenseException(OUSTR("User declined the license."), 0, m_name, sLicense))); 726 } 727 return true; 728 } catch (css::ucb::CommandFailedException&) { 729 throw; 730 } catch (css::ucb::CommandAbortedException&) { 731 throw; 732 } catch (css::deployment::DeploymentException&) { 733 throw; 734 } catch (css::uno::RuntimeException&) { 735 throw; 736 } catch (css::uno::Exception&) { 737 Any anyExc = cppu::getCaughtException(); 738 throw css::deployment::DeploymentException(OUSTR("Unexpected exception"), 0, anyExc); 739 } 740 } 741 742 ::sal_Int32 BackendImpl::PackageImpl::checkPrerequisites( 743 const css::uno::Reference< css::task::XAbortChannel >&, 744 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv, 745 sal_Bool alreadyInstalled) 746 throw (css::deployment::DeploymentException, 747 css::deployment::ExtensionRemovedException, 748 css::ucb::CommandFailedException, 749 css::ucb::CommandAbortedException, 750 css::uno::RuntimeException) 751 { 752 if (m_bRemoved) 753 throw deployment::ExtensionRemovedException(); 754 DescriptionInfoset info = getDescriptionInfoset(); 755 if (!info.hasDescription()) 756 return 0; 757 758 //always return LICENSE as long as the user did not accept the license 759 //so that XExtensonManager::checkPrerequisitesAndEnable will again 760 //check the license 761 if (!checkPlatform(xCmdEnv)) 762 return deployment::Prerequisites::PLATFORM | 763 deployment::Prerequisites::LICENSE; 764 else if(!checkDependencies(xCmdEnv, info)) 765 return deployment::Prerequisites::DEPENDENCIES | 766 deployment::Prerequisites::LICENSE; 767 else if(!checkLicense(xCmdEnv, info, alreadyInstalled)) 768 return deployment::Prerequisites::LICENSE; 769 else 770 return 0; 771 } 772 773 ::sal_Bool BackendImpl::PackageImpl::checkDependencies( 774 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv ) 775 throw (deployment::DeploymentException, 776 deployment::ExtensionRemovedException, 777 ucb::CommandFailedException, 778 RuntimeException) 779 { 780 if (m_bRemoved) 781 throw deployment::ExtensionRemovedException(); 782 DescriptionInfoset info = getDescriptionInfoset(); 783 if (!info.hasDescription()) 784 return sal_True; 785 786 return checkDependencies(xCmdEnv, info); 787 } 788 789 beans::Optional<OUString> BackendImpl::PackageImpl::getIdentifier() 790 throw (RuntimeException) 791 { 792 OUString identifier; 793 if (m_bRemoved) 794 identifier = m_identifier; 795 else 796 identifier = dp_misc::generateIdentifier( 797 getDescriptionInfoset().getIdentifier(), m_name); 798 799 return beans::Optional<OUString>( 800 true, identifier); 801 } 802 803 OUString BackendImpl::PackageImpl::getVersion() 804 throw (deployment::ExtensionRemovedException, RuntimeException) 805 { 806 if (m_bRemoved) 807 throw deployment::ExtensionRemovedException(); 808 return getDescriptionInfoset().getVersion(); 809 } 810 811 Sequence<OUString> BackendImpl::PackageImpl::getUpdateInformationURLs() 812 throw (deployment::ExtensionRemovedException, RuntimeException) 813 { 814 if (m_bRemoved) 815 throw deployment::ExtensionRemovedException(); 816 return getDescriptionInfoset().getUpdateInformationUrls(); 817 } 818 819 beans::StringPair BackendImpl::PackageImpl::getPublisherInfo() 820 throw (deployment::ExtensionRemovedException, RuntimeException) 821 { 822 if (m_bRemoved) 823 throw deployment::ExtensionRemovedException(); 824 ::std::pair< OUString, OUString > aInfo = getDescriptionInfoset().getLocalizedPublisherNameAndURL(); 825 beans::StringPair aStrPair( aInfo.first, aInfo.second ); 826 return aStrPair; 827 } 828 829 //______________________________________________________________________________ 830 uno::Reference< graphic::XGraphic > BackendImpl::PackageImpl::getIcon( sal_Bool bHighContrast ) 831 throw (deployment::ExtensionRemovedException, RuntimeException ) 832 { 833 if (m_bRemoved) 834 throw deployment::ExtensionRemovedException(); 835 836 uno::Reference< graphic::XGraphic > xGraphic; 837 838 OUString aIconURL = getDescriptionInfoset().getIconURL( bHighContrast ); 839 if ( aIconURL.getLength() ) 840 { 841 OUString aFullIconURL = m_url_expanded + OUSTR("/") + aIconURL; 842 843 uno::Reference< XComponentContext > xContext( getMyBackend()->getComponentContext() ); 844 uno::Reference< graphic::XGraphicProvider > xGraphProvider( 845 xContext->getServiceManager()->createInstanceWithContext( OUSTR( "com.sun.star.graphic.GraphicProvider" ), xContext ), 846 uno::UNO_QUERY ); 847 848 if ( xGraphProvider.is() ) 849 { 850 uno::Sequence< beans::PropertyValue > aMediaProps( 1 ); 851 aMediaProps[0].Name = OUSTR( "URL" ); 852 aMediaProps[0].Value <<= aFullIconURL; 853 854 xGraphic = xGraphProvider->queryGraphic( aMediaProps ); 855 } 856 } 857 858 return xGraphic; 859 } 860 861 //______________________________________________________________________________ 862 void BackendImpl::PackageImpl::processPackage_( 863 ::osl::ResettableMutexGuard &, 864 bool doRegisterPackage, 865 bool startup, 866 ::rtl::Reference<AbortChannel> const & abortChannel, 867 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) 868 { 869 const Sequence< Reference<deployment::XPackage> > bundle( 870 getBundle( abortChannel.get(), xCmdEnv ) ); 871 872 if (doRegisterPackage) 873 { 874 ExtensionBackendDb::Data data; 875 const sal_Int32 len = bundle.getLength(); 876 for ( sal_Int32 pos = 0; pos < len; ++pos ) 877 { 878 checkAborted(abortChannel); 879 Reference<deployment::XPackage> const & xPackage = bundle[ pos ]; 880 Reference<task::XAbortChannel> xSubAbortChannel( 881 xPackage->createAbortChannel() ); 882 AbortChannel::Chain chain( abortChannel, xSubAbortChannel ); 883 try { 884 xPackage->registerPackage( startup, xSubAbortChannel, xCmdEnv ); 885 } 886 catch (Exception &) 887 { 888 //We even try a rollback if the user cancelled the action (CommandAbortedException) 889 //in order to prevent invalid database entries. 890 Any exc( ::cppu::getCaughtException() ); 891 // try to handle exception, notify: 892 bool approve = false, abort = false; 893 if (! interactContinuation( 894 Any( lang::WrappedTargetException( 895 OUSTR("bundle item registration error!"), 896 static_cast<OWeakObject *>(this), exc ) ), 897 task::XInteractionApprove::static_type(), xCmdEnv, 898 &approve, &abort )) { 899 OSL_ASSERT( !approve && !abort ); 900 if (m_legacyBundle) // default for legacy packages: ignore 901 continue; 902 // no selection at all, so rethrow; 903 // no C++ rethrow after getCaughtException(), 904 // see cppuhelper/exc_hlp.hxx: 905 ::cppu::throwException(exc); 906 } 907 if (approve && !abort) // ignore error, just continue 908 continue; 909 910 { 911 ProgressLevel progress( 912 xCmdEnv, OUSTR("rollback...") ); 913 // try rollback 914 for ( ; pos--; ) 915 { 916 try { 917 bundle[ pos ]->revokePackage( 918 xSubAbortChannel, xCmdEnv ); 919 } 920 catch (Exception &) 921 { 922 OSL_ENSURE( 0, ::rtl::OUStringToOString( 923 ::comphelper::anyToString( 924 ::cppu::getCaughtException() ), 925 RTL_TEXTENCODING_UTF8 ).getStr() ); 926 // ignore any errors of rollback 927 } 928 } 929 progress.update( OUSTR("rollback finished.") ); 930 } 931 932 deployment::DeploymentException dpExc; 933 if (exc >>= dpExc) { 934 throw ucb::CommandFailedException( 935 dpExc.Message, dpExc.Context, dpExc.Cause ); 936 } 937 else { 938 // rethrow CommandFailedException 939 ::cppu::throwException(exc); 940 } 941 } 942 data.items.push_back( 943 ::std::make_pair(xPackage->getURL(), 944 xPackage->getPackageType()->getMediaType())); 945 } 946 getMyBackend()->addDataToDb(getURL(), data); 947 } 948 else 949 { 950 // revoke in reverse order: 951 for ( sal_Int32 pos = bundle.getLength(); pos--; ) 952 { 953 checkAborted(abortChannel); 954 Reference<deployment::XPackage> const & xPackage = bundle[ pos ]; 955 Reference<task::XAbortChannel> xSubAbortChannel( 956 xPackage->createAbortChannel() ); 957 AbortChannel::Chain chain( abortChannel, xSubAbortChannel ); 958 try { 959 bundle[ pos ]->revokePackage( xSubAbortChannel, xCmdEnv ); 960 } 961 catch (RuntimeException &) { 962 throw; 963 } 964 catch (ucb::CommandAbortedException &) { 965 throw; 966 } 967 catch (Exception &) { 968 // CommandFailedException, DeploymentException: 969 Any exc( ::cppu::getCaughtException() ); 970 // try to handle exception, notify: 971 bool approve = false, abort = false; 972 if (! interactContinuation( 973 Any( lang::WrappedTargetException( 974 OUSTR("bundle item revocation error!"), 975 static_cast<OWeakObject *>(this), exc ) ), 976 task::XInteractionApprove::static_type(), xCmdEnv, 977 &approve, &abort )) { 978 OSL_ASSERT( !approve && !abort ); 979 if (m_legacyBundle) // default for legacy packages: ignore 980 continue; 981 // no selection at all, so rethrow 982 // no C++ rethrow after getCaughtException(), 983 // see cppuhelper/exc_hlp.hxx: 984 ::cppu::throwException(exc); 985 } 986 // ignore errors when revoking, although abort may have been 987 // selected 988 } 989 } 990 getMyBackend()->revokeEntryFromDb(getURL()); 991 } 992 } 993 994 //______________________________________________________________________________ 995 OUString BackendImpl::PackageImpl::getDescription() 996 throw (deployment::ExtensionRemovedException, RuntimeException) 997 { 998 if (m_bRemoved) 999 throw deployment::ExtensionRemovedException(); 1000 1001 const OUString sRelativeURL(getDescriptionInfoset().getLocalizedDescriptionURL()); 1002 OUString sDescription; 1003 if (sRelativeURL.getLength()) 1004 { 1005 OUString sURL = m_url_expanded + OUSTR("/") + sRelativeURL; 1006 1007 try 1008 { 1009 sDescription = getTextFromURL( css::uno::Reference< css::ucb::XCommandEnvironment >(), sURL ); 1010 } 1011 catch ( css::deployment::DeploymentException& ) 1012 { 1013 OSL_ENSURE( 0, ::rtl::OUStringToOString( ::comphelper::anyToString( ::cppu::getCaughtException() ), RTL_TEXTENCODING_UTF8 ).getStr() ); 1014 } 1015 } 1016 1017 if (sDescription.getLength()) 1018 return sDescription; 1019 return m_oldDescription; 1020 } 1021 1022 //______________________________________________________________________________ 1023 OUString BackendImpl::PackageImpl::getLicenseText() 1024 throw (deployment::ExtensionRemovedException, RuntimeException) 1025 { 1026 if (m_bRemoved) 1027 throw deployment::ExtensionRemovedException(); 1028 1029 OUString sLicense; 1030 DescriptionInfoset aInfo = getDescriptionInfoset(); 1031 1032 ::boost::optional< SimpleLicenseAttributes > aSimplLicAttr = aInfo.getSimpleLicenseAttributes(); 1033 if ( aSimplLicAttr ) 1034 { 1035 OUString aLicenseURL = aInfo.getLocalizedLicenseURL(); 1036 1037 if ( aLicenseURL.getLength() ) 1038 { 1039 OUString aFullURL = m_url_expanded + OUSTR("/") + aLicenseURL; 1040 sLicense = getTextFromURL( Reference< ucb::XCommandEnvironment >(), aFullURL); 1041 } 1042 } 1043 1044 return sLicense; 1045 } 1046 1047 //______________________________________________________________________________ 1048 void BackendImpl::PackageImpl::exportTo( 1049 OUString const & destFolderURL, OUString const & newTitle, 1050 sal_Int32 nameClashAction, Reference<ucb::XCommandEnvironment> const & xCmdEnv ) 1051 throw (ucb::CommandFailedException, 1052 deployment::ExtensionRemovedException, 1053 ucb::CommandAbortedException, RuntimeException) 1054 { 1055 if (m_bRemoved) 1056 throw deployment::ExtensionRemovedException(); 1057 1058 ::ucbhelper::Content sourceContent( m_url_expanded, xCmdEnv ); 1059 OUString title(newTitle); 1060 if (title.getLength() == 0) 1061 sourceContent.getPropertyValue( StrTitle::get() ) >>= title; 1062 OUString destURL( makeURL( destFolderURL, ::rtl::Uri::encode( 1063 title, rtl_UriCharClassPchar, 1064 rtl_UriEncodeIgnoreEscapes, 1065 RTL_TEXTENCODING_UTF8 ) ) ); 1066 1067 if (nameClashAction == ucb::NameClash::ASK) 1068 { 1069 if (create_ucb_content( 1070 0, destURL, xCmdEnv, false /* no throw */ )) { 1071 bool replace = false, abort = false; 1072 if (! interactContinuation( 1073 Any( ucb::NameClashResolveRequest( 1074 OUSTR("file already exists: ") + title, 1075 static_cast<OWeakObject *>(this), 1076 task::InteractionClassification_QUERY, 1077 destFolderURL, title, OUString() ) ), 1078 ucb::XInteractionReplaceExistingData::static_type(), xCmdEnv, 1079 &replace, &abort ) || !replace) { 1080 return; 1081 } 1082 } 1083 } 1084 else if (nameClashAction != ucb::NameClash::OVERWRITE) { 1085 throw ucb::CommandFailedException( 1086 OUSTR("unsupported nameClashAction!"), 1087 static_cast<OWeakObject *>(this), Any() ); 1088 } 1089 erase_path( destURL, xCmdEnv ); 1090 1091 ::rtl::OUStringBuffer buf; 1092 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.zip://") ); 1093 buf.append( ::rtl::Uri::encode( destURL, 1094 rtl_UriCharClassRegName, 1095 rtl_UriEncodeIgnoreEscapes, 1096 RTL_TEXTENCODING_UTF8 ) ); 1097 buf.append( static_cast<sal_Unicode>('/') ); 1098 OUString destFolder( buf.makeStringAndClear() ); 1099 1100 ::ucbhelper::Content destFolderContent( destFolder, xCmdEnv ); 1101 { 1102 // transfer every item of folder into zip: 1103 Reference<sdbc::XResultSet> xResultSet( 1104 sourceContent.createCursor( 1105 Sequence<OUString>(), 1106 ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS ) ); 1107 ProgressLevel progress( xCmdEnv, OUString() ); 1108 while (xResultSet->next()) 1109 { 1110 ::ucbhelper::Content subContent( 1111 Reference<ucb::XContentAccess>( 1112 xResultSet, UNO_QUERY_THROW )->queryContent(), xCmdEnv ); 1113 if (! destFolderContent.transferContent( 1114 subContent, ::ucbhelper::InsertOperation_COPY, 1115 OUString(), ucb::NameClash::OVERWRITE )) 1116 throw RuntimeException( OUSTR("UCB transferContent() failed!"), 1117 static_cast<OWeakObject *>(this) ); 1118 progress.update( Any() ); // animating progress bar 1119 } 1120 } 1121 1122 // assure META-INF folder: 1123 ::ucbhelper::Content metainfFolderContent; 1124 create_folder( &metainfFolderContent, 1125 makeURL( destFolderContent.getURL(), OUSTR("META-INF") ), 1126 xCmdEnv ); 1127 1128 if (m_legacyBundle) 1129 { 1130 // easy to migrate legacy bundles to new format: 1131 // just export them once using a .oxt name! 1132 // set detected media-types of any bundle item: 1133 1134 // collect all manifest entries: 1135 Sequence< Reference<deployment::XPackage> > bundle; 1136 try { 1137 bundle = getBundle( Reference<task::XAbortChannel>(), xCmdEnv ); 1138 } 1139 // xxx todo: think about exception specs: 1140 catch (deployment::DeploymentException &) { 1141 OSL_ENSURE( 0, ::rtl::OUStringToOString( 1142 ::comphelper::anyToString( 1143 ::cppu::getCaughtException() ), 1144 RTL_TEXTENCODING_UTF8 ).getStr() ); 1145 } 1146 catch (lang::IllegalArgumentException & exc) { 1147 (void) exc; 1148 OSL_ENSURE( 0, ::rtl::OUStringToOString( 1149 exc.Message, RTL_TEXTENCODING_UTF8 ).getStr() ); 1150 } 1151 1152 ::std::vector< Sequence<beans::PropertyValue> > manifest; 1153 manifest.reserve( bundle.getLength() ); 1154 sal_Int32 baseURLlen = m_url_expanded.getLength(); 1155 Reference<deployment::XPackage> const *pbundle = bundle.getConstArray(); 1156 const OUString strMediaType = OUSTR("MediaType"); 1157 const OUString strFullPath = OUSTR("FullPath"); 1158 const OUString strIsFolder = OUSTR("IsFolder"); 1159 for ( sal_Int32 pos = bundle.getLength(); pos--; ) 1160 { 1161 Reference<deployment::XPackage> const & xPackage = pbundle[ pos ]; 1162 OUString url_( expandUnoRcUrl( xPackage->getURL() ) ); 1163 OSL_ASSERT( url_.getLength() >= baseURLlen ); 1164 OUString fullPath; 1165 if (url_.getLength() > baseURLlen) 1166 fullPath = url_.copy( baseURLlen + 1 ); 1167 ::ucbhelper::Content ucbContent( url_, xCmdEnv ); 1168 if (ucbContent.getPropertyValue(strIsFolder).get<bool>()) 1169 fullPath += OUSTR("/"); 1170 Sequence<beans::PropertyValue> attribs( 2 ); 1171 beans::PropertyValue * pattribs = attribs.getArray(); 1172 pattribs[ 0 ].Name = strFullPath; 1173 pattribs[ 0 ].Value <<= fullPath; 1174 pattribs[ 1 ].Name = strMediaType; 1175 const Reference<deployment::XPackageTypeInfo> xPackageType( 1176 xPackage->getPackageType() ); 1177 OUString mediaType; 1178 OSL_ASSERT( xPackageType.is() ); 1179 if (xPackageType.is()) 1180 mediaType = xPackageType->getMediaType(); 1181 else 1182 mediaType = OUSTR("unknown"); 1183 pattribs[ 1 ].Value <<= mediaType; 1184 manifest.push_back( attribs ); 1185 } 1186 1187 // write into pipe: 1188 Reference<XComponentContext> xContext( 1189 getMyBackend()->getComponentContext() ); 1190 Reference<packages::manifest::XManifestWriter> xManifestWriter( 1191 xContext->getServiceManager()->createInstanceWithContext( 1192 OUSTR("com.sun.star.packages.manifest.ManifestWriter"), 1193 xContext ), UNO_QUERY_THROW ); 1194 Reference<io::XOutputStream> xPipe( 1195 xContext->getServiceManager()->createInstanceWithContext( 1196 OUSTR("com.sun.star.io.Pipe"), xContext ), UNO_QUERY_THROW ); 1197 xManifestWriter->writeManifestSequence( 1198 xPipe, comphelper::containerToSequence(manifest) ); 1199 1200 // write buffered pipe data to content: 1201 ::ucbhelper::Content manifestContent( 1202 makeURL( metainfFolderContent.getURL(), OUSTR("manifest.xml") ), 1203 xCmdEnv ); 1204 manifestContent.writeStream( 1205 Reference<io::XInputStream>( xPipe, UNO_QUERY_THROW ), 1206 true /* replace existing */ ); 1207 } 1208 else 1209 { 1210 // overwrite manifest.xml: 1211 ::ucbhelper::Content manifestContent; 1212 if ( ! create_ucb_content( 1213 &manifestContent, 1214 makeURL( m_url_expanded, OUSTR("META-INF/manifest.xml") ), 1215 xCmdEnv, false ) ) 1216 { 1217 OSL_ENSURE( 0, "### missing META-INF/manifest.xml file!" ); 1218 return; 1219 } 1220 1221 if (! metainfFolderContent.transferContent( 1222 manifestContent, ::ucbhelper::InsertOperation_COPY, 1223 OUString(), ucb::NameClash::OVERWRITE )) 1224 throw RuntimeException( OUSTR("UCB transferContent() failed!"), 1225 static_cast<OWeakObject *>(this) ); 1226 } 1227 1228 // xxx todo: maybe obsolete in the future 1229 try { 1230 destFolderContent.executeCommand( OUSTR("flush"), Any() ); 1231 } 1232 catch (ucb::UnsupportedCommandException &) { 1233 } 1234 } 1235 1236 //______________________________________________________________________________ 1237 sal_Bool BackendImpl::PackageImpl::isBundle() throw (RuntimeException) 1238 { 1239 return true; 1240 } 1241 1242 //______________________________________________________________________________ 1243 Sequence< Reference<deployment::XPackage> > BackendImpl::PackageImpl::getBundle( 1244 Reference<task::XAbortChannel> const & xAbortChannel, 1245 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) 1246 throw (deployment::DeploymentException, 1247 ucb::CommandFailedException, ucb::CommandAbortedException, 1248 lang::IllegalArgumentException, RuntimeException) 1249 { 1250 Sequence< Reference<deployment::XPackage> > * pBundle = m_pBundle; 1251 if (pBundle == 0) 1252 { 1253 t_packagevec bundle; 1254 if (m_bRemoved) 1255 { 1256 bundle = getPackagesFromDb(xCmdEnv); 1257 } 1258 else 1259 { 1260 try { 1261 if (m_legacyBundle) 1262 { 1263 // .zip legacy packages allow script.xlb, dialog.xlb in bundle 1264 // root folder: 1265 OUString mediaType; 1266 // probe for script.xlb: 1267 if (create_ucb_content( 1268 0, makeURL( m_url_expanded, OUSTR("script.xlb") ), 1269 xCmdEnv, false /* no throw */ )) { 1270 mediaType = OUSTR("application/vnd.sun.star.basic-library"); 1271 } 1272 // probe for dialog.xlb: 1273 else if (create_ucb_content( 1274 0, makeURL( m_url_expanded, OUSTR("dialog.xlb") ), 1275 xCmdEnv, false /* no throw */ )) 1276 mediaType = OUSTR("application/vnd.sun.star." 1277 "dialog-library"); 1278 1279 if (mediaType.getLength() > 0) { 1280 const Reference<deployment::XPackage> xPackage( 1281 bindBundleItem( getURL(), mediaType, false, OUString(), 1282 xCmdEnv ) ); 1283 if (xPackage.is()) 1284 bundle.push_back( xPackage ); 1285 // continue scanning: 1286 } 1287 scanLegacyBundle( bundle, getURL(), 1288 AbortChannel::get(xAbortChannel), xCmdEnv ); 1289 } 1290 else 1291 { 1292 // .oxt: 1293 scanBundle( bundle, AbortChannel::get(xAbortChannel), xCmdEnv ); 1294 } 1295 1296 } 1297 catch (RuntimeException &) { 1298 throw; 1299 } 1300 catch (ucb::CommandFailedException &) { 1301 throw; 1302 } 1303 catch (ucb::CommandAbortedException &) { 1304 throw; 1305 } 1306 catch (deployment::DeploymentException &) { 1307 throw; 1308 } 1309 catch (Exception &) { 1310 Any exc( ::cppu::getCaughtException() ); 1311 throw deployment::DeploymentException( 1312 OUSTR("error scanning bundle: ") + getURL(), 1313 static_cast<OWeakObject *>(this), exc ); 1314 } 1315 } 1316 1317 // sort: schema before config data, typelibs before components: 1318 Sequence< Reference<deployment::XPackage> > ret( bundle.size() ); 1319 Reference<deployment::XPackage> * pret = ret.getArray(); 1320 sal_Int32 lower_end = 0; 1321 sal_Int32 upper_end = ret.getLength(); 1322 t_packagevec::const_iterator iPos( bundle.begin() ); 1323 t_packagevec::const_iterator const iEnd( bundle.end() ); 1324 for ( ; iPos != iEnd; ++iPos ) 1325 { 1326 const Reference<deployment::XPackageTypeInfo> xPackageType( 1327 (*iPos)->getPackageType() ); 1328 OSL_ASSERT( xPackageType.is() ); 1329 if (xPackageType.is()) { 1330 const OUString mediaType( xPackageType->getMediaType() ); 1331 String type, subType; 1332 INetContentTypeParameterList params; 1333 if (INetContentTypes::parse( 1334 mediaType, type, subType, ¶ms ) && 1335 type.EqualsIgnoreCaseAscii("application") && 1336 (subType.EqualsIgnoreCaseAscii( 1337 "vnd.sun.star.uno-component") || 1338 subType.EqualsIgnoreCaseAscii( 1339 "vnd.sun.star.configuration-data"))) 1340 { 1341 --upper_end; 1342 pret[ upper_end ] = *iPos; 1343 continue; 1344 } 1345 } 1346 pret[ lower_end ] = *iPos; 1347 ++lower_end; 1348 } 1349 OSL_ASSERT( lower_end == upper_end ); 1350 1351 const ::osl::MutexGuard guard( getMutex() ); 1352 pBundle = m_pBundle; 1353 if (pBundle == 0) { 1354 m_bundle = ret; 1355 pBundle = &m_bundle; 1356 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); 1357 m_pBundle = pBundle; 1358 } 1359 } 1360 else { 1361 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); 1362 } 1363 return *pBundle; 1364 } 1365 1366 inline bool isBundle_( OUString const & mediaType ) 1367 { 1368 // xxx todo: additional parsing? 1369 return mediaType.getLength() > 0 && 1370 (mediaType.matchIgnoreAsciiCaseAsciiL( 1371 RTL_CONSTASCII_STRINGPARAM( 1372 "application/vnd.sun.star.package-bundle") ) || 1373 mediaType.matchIgnoreAsciiCaseAsciiL( 1374 RTL_CONSTASCII_STRINGPARAM( 1375 "application/vnd.sun.star.legacy-package-bundle") )); 1376 } 1377 1378 //______________________________________________________________________________ 1379 Reference<deployment::XPackage> BackendImpl::PackageImpl::bindBundleItem( 1380 OUString const & url, OUString const & mediaType, 1381 sal_Bool bRemoved, OUString const & identifier, 1382 Reference<ucb::XCommandEnvironment> const & xCmdEnv, 1383 bool notifyDetectionError ) 1384 { 1385 // ignore any nested bundles: 1386 if (isBundle_(mediaType)) 1387 return Reference<deployment::XPackage>(); 1388 1389 Reference<deployment::XPackage>xPackage; 1390 try { 1391 xPackage.set( getMyBackend()->m_xRootRegistry->bindPackage( 1392 url, mediaType, bRemoved, identifier, xCmdEnv ) ); 1393 OSL_ASSERT( xPackage.is() ); 1394 } 1395 catch (RuntimeException &) { 1396 throw; 1397 } 1398 catch (ucb::CommandFailedException &) { 1399 // ignore already handled error 1400 } 1401 catch (Exception &) { 1402 const Any exc( ::cppu::getCaughtException() ); 1403 if (notifyDetectionError || 1404 !exc.isExtractableTo( 1405 ::getCppuType( reinterpret_cast< 1406 lang::IllegalArgumentException const *>(0) ) )) 1407 { 1408 interactContinuation( 1409 Any( lang::WrappedTargetException( 1410 OUSTR("bundle item error!"), 1411 static_cast<OWeakObject *>(this), exc ) ), 1412 task::XInteractionApprove::static_type(), xCmdEnv, 0, 0 ); 1413 } 1414 } 1415 1416 if (xPackage.is()) { 1417 const Reference<deployment::XPackageTypeInfo> xPackageType( 1418 xPackage->getPackageType() ); 1419 OSL_ASSERT( xPackageType.is() ); 1420 // ignore any nested bundles: 1421 if (xPackageType.is() && isBundle_( xPackageType->getMediaType() )) 1422 xPackage.clear(); 1423 } 1424 return xPackage; 1425 } 1426 1427 //______________________________________________________________________________ 1428 void BackendImpl::PackageImpl::scanBundle( 1429 t_packagevec & bundle, 1430 ::rtl::Reference<AbortChannel> const & abortChannel, 1431 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) 1432 { 1433 OSL_ASSERT( !m_legacyBundle ); 1434 1435 ::ucbhelper::Content manifestContent; 1436 if (! create_ucb_content( 1437 &manifestContent, 1438 makeURL( m_url_expanded, OUSTR("META-INF/manifest.xml") ), 1439 xCmdEnv, false /* no throw */ )) 1440 { 1441 OSL_ENSURE( 0, "### missing META-INF/manifest.xml file!" ); 1442 return; 1443 } 1444 1445 1446 const lang::Locale officeLocale = getOfficeLocale(); 1447 OUString descrFile; 1448 lang::Locale descrFileLocale; 1449 1450 const Reference<XComponentContext> xContext( 1451 getMyBackend()->getComponentContext() ); 1452 Reference<packages::manifest::XManifestReader> xManifestReader( 1453 xContext->getServiceManager()->createInstanceWithContext( 1454 OUSTR("com.sun.star.packages.manifest.ManifestReader"), 1455 xContext ), UNO_QUERY_THROW ); 1456 const Sequence< Sequence<beans::PropertyValue> > manifestSeq( 1457 xManifestReader->readManifestSequence( manifestContent.openStream() ) ); 1458 const OUString packageRootURL( getURL() ); 1459 for ( sal_Int32 pos = manifestSeq.getLength(); pos--; ) 1460 { 1461 OUString fullPath, mediaType; 1462 Sequence<beans::PropertyValue> const & attribs = manifestSeq[ pos ]; 1463 for ( sal_Int32 i = attribs.getLength(); i--; ) 1464 { 1465 if (fullPath.getLength() > 0 && mediaType.getLength() > 0) 1466 break; 1467 if (attribs[i].Name.equalsAsciiL( 1468 RTL_CONSTASCII_STRINGPARAM("FullPath") )) 1469 attribs[i].Value >>= fullPath; 1470 else if (attribs[i].Name.equalsAsciiL( 1471 RTL_CONSTASCII_STRINGPARAM("MediaType") )) 1472 attribs[i].Value >>= mediaType; 1473 } 1474 1475 if (fullPath.getLength() == 0 || mediaType.getLength() == 0 || 1476 mediaType.equalsAsciiL( // opt: exclude common text/xml 1477 RTL_CONSTASCII_STRINGPARAM("text/xml") )) 1478 continue; 1479 1480 String type, subType; 1481 INetContentTypeParameterList params; 1482 if (! INetContentTypes::parse( mediaType, type, subType, ¶ms )) 1483 continue; 1484 1485 INetContentTypeParameter const * param = params.find( 1486 ByteString("platform") ); 1487 if (param != 0 && !platform_fits( param->m_sValue )) 1488 continue; 1489 const OUString url( makeURL( packageRootURL, fullPath ) ); 1490 1491 // check for bundle description: 1492 if (type.EqualsIgnoreCaseAscii("application") && 1493 subType.EqualsIgnoreCaseAscii( 1494 "vnd.sun.star.package-bundle-description")) 1495 { 1496 // check locale: 1497 param = params.find( ByteString("locale") ); 1498 if (param == 0) { 1499 if (descrFile.getLength() == 0) 1500 descrFile = url; 1501 } 1502 else { 1503 // match best locale: 1504 lang::Locale locale( toLocale(param->m_sValue) ); 1505 if (locale.Language == officeLocale.Language) 1506 { 1507 if (descrFileLocale.Country == officeLocale.Country 1508 && locale.Country != officeLocale.Country) 1509 continue; 1510 if (descrFileLocale.Variant == officeLocale.Variant 1511 && locale.Variant != officeLocale.Variant) 1512 continue; 1513 descrFile = url; 1514 descrFileLocale = locale; 1515 } 1516 } 1517 continue; 1518 } 1519 1520 checkAborted( abortChannel ); 1521 1522 //We make sure that we only create one XPackage for a particular URL. 1523 //Sometime programmers insert the same URL several times in the manifest 1524 //which may lead to DisposedExceptions. 1525 if (bundle.end() == std::find_if(bundle.begin(), bundle.end(), XPackage_eq(url))) 1526 { 1527 const Reference<deployment::XPackage> xPackage( 1528 bindBundleItem( url, mediaType, false, OUString(), xCmdEnv ) ); 1529 if (xPackage.is()) 1530 bundle.push_back( xPackage ); 1531 } 1532 else 1533 { 1534 fprintf(stderr, "manifest.xml contains a duplicate entry!\n"); 1535 } 1536 } 1537 1538 if (descrFile.getLength() > 0) 1539 { 1540 ::ucbhelper::Content descrFileContent; 1541 if (create_ucb_content( &descrFileContent, descrFile, 1542 xCmdEnv, false /* no throw */ )) 1543 { 1544 // patch description: 1545 ::rtl::ByteSequence bytes( readFile( descrFileContent ) ); 1546 ::rtl::OUStringBuffer buf; 1547 if ( bytes.getLength() ) 1548 { 1549 buf.append( OUString( reinterpret_cast<sal_Char const *>( 1550 bytes.getConstArray() ), 1551 bytes.getLength(), RTL_TEXTENCODING_UTF8 ) ); 1552 } 1553 else 1554 { 1555 buf.append( Package::getDescription() ); 1556 } 1557 m_oldDescription = buf.makeStringAndClear(); 1558 } 1559 } 1560 } 1561 1562 //______________________________________________________________________________ 1563 void BackendImpl::PackageImpl::scanLegacyBundle( 1564 t_packagevec & bundle, 1565 OUString const & url, 1566 ::rtl::Reference<AbortChannel> const & abortChannel, 1567 Reference<ucb::XCommandEnvironment> const & xCmdEnv, 1568 bool skip_registration ) 1569 { 1570 ::ucbhelper::Content ucbContent( url, xCmdEnv ); 1571 1572 // check for platform pathes: 1573 const OUString title( ucbContent.getPropertyValue( 1574 StrTitle::get() ).get<OUString>() ); 1575 if (title.endsWithIgnoreAsciiCaseAsciiL( 1576 RTL_CONSTASCII_STRINGPARAM(".plt") ) && 1577 !platform_fits( title.copy( 0, title.getLength() - 4 ) )) { 1578 return; 1579 } 1580 if (title.endsWithIgnoreAsciiCaseAsciiL( 1581 RTL_CONSTASCII_STRINGPARAM("skip_registration") )) 1582 skip_registration = true; 1583 1584 OUString ar [] = { StrTitle::get(), OUSTR("IsFolder") }; 1585 Reference<sdbc::XResultSet> xResultSet( 1586 ucbContent.createCursor( 1587 Sequence<OUString>( ar, ARLEN(ar) ), 1588 ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS ) ); 1589 while (xResultSet->next()) 1590 { 1591 checkAborted( abortChannel ); 1592 1593 const Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW ); 1594 const OUString title_enc( ::rtl::Uri::encode( 1595 xRow->getString( 1 /* Title */ ), 1596 rtl_UriCharClassPchar, 1597 rtl_UriEncodeIgnoreEscapes, 1598 RTL_TEXTENCODING_UTF8 ) ); 1599 const OUString path( makeURL( url, title_enc ) ); 1600 1601 OUString mediaType; 1602 const Reference<deployment::XPackage> xPackage( 1603 bindBundleItem( path, OUString() /* detect */, false, OUString(), 1604 xCmdEnv, false /* ignore detection errors */ ) ); 1605 if (xPackage.is()) { 1606 const Reference<deployment::XPackageTypeInfo> xPackageType( 1607 xPackage->getPackageType() ); 1608 OSL_ASSERT( xPackageType.is() ); 1609 if (xPackageType.is()) 1610 mediaType = xPackageType->getMediaType(); 1611 1612 if (skip_registration && 1613 // xxx todo: additional parsing? 1614 mediaType.matchIgnoreAsciiCaseAsciiL( 1615 RTL_CONSTASCII_STRINGPARAM( 1616 "application/vnd.sun.star.uno-component") )) 1617 continue; 1618 1619 bundle.push_back( xPackage ); 1620 } 1621 1622 if (mediaType.getLength() == 0 || 1623 // script.xlb, dialog.xlb can be met everywhere: 1624 mediaType.matchIgnoreAsciiCaseAsciiL( 1625 RTL_CONSTASCII_STRINGPARAM( 1626 "application/vnd.sun.star.basic-library") ) || 1627 mediaType.matchIgnoreAsciiCaseAsciiL( 1628 RTL_CONSTASCII_STRINGPARAM( 1629 "application/vnd.sun.star.dialog-library") )) 1630 { 1631 if (xRow->getBoolean( 2 /* IsFolder */ )) { // recurse into folder: 1632 scanLegacyBundle( 1633 bundle, path, abortChannel, xCmdEnv, skip_registration ); 1634 } 1635 } 1636 } 1637 } 1638 1639 OUString BackendImpl::PackageImpl::getDisplayName() 1640 throw (deployment::ExtensionRemovedException, RuntimeException) 1641 { 1642 if (m_bRemoved) 1643 throw deployment::ExtensionRemovedException(); 1644 1645 OUString sName = getDescriptionInfoset().getLocalizedDisplayName(); 1646 if (sName.getLength() == 0) 1647 return m_displayName; 1648 else 1649 return sName; 1650 } 1651 1652 ::std::vector<Reference<deployment::XPackage> > 1653 BackendImpl::PackageImpl::getPackagesFromDb( 1654 Reference<ucb::XCommandEnvironment> const & xCmdEnv) 1655 { 1656 ::std::vector<Reference<deployment::XPackage> > retVector; 1657 1658 typedef ::std::vector< ::std::pair<OUString, OUString> >::const_iterator ITC; 1659 for (ITC i = m_dbData.items.begin(); i != m_dbData.items.end(); i++) 1660 { 1661 Reference<deployment::XPackage> xExtension = 1662 bindBundleItem(i->first, i->second, true, m_identifier, xCmdEnv); 1663 OSL_ASSERT(xExtension.is()); 1664 retVector.push_back(xExtension); 1665 } 1666 1667 return retVector; 1668 } 1669 1670 } // anon namespace 1671 1672 //============================================================================== 1673 Reference<deployment::XPackageRegistry> create( 1674 Reference<deployment::XPackageRegistry> const & xRootRegistry, 1675 OUString const & context, OUString const & cachePath, bool readOnly, 1676 Reference<XComponentContext> const & xComponentContext ) 1677 { 1678 Sequence<Any> args( 1679 cachePath.getLength() == 0 ? 1 : 3 ); 1680 args[ 0 ] <<= context; 1681 if (cachePath.getLength() > 0) { 1682 args[ 1 ] <<= cachePath; 1683 args[ 2 ] <<= readOnly; 1684 } 1685 return new BackendImpl( args, xComponentContext, xRootRegistry ); 1686 } 1687 1688 } // namespace bundle 1689 } // namespace backend 1690 } // namespace dp_registry 1691 1692