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 "oo3extensionmigration.hxx" 28 #include <rtl/instance.hxx> 29 #include <osl/file.hxx> 30 #include <osl/thread.h> 31 #include <tools/urlobj.hxx> 32 #include <unotools/bootstrap.hxx> 33 #include <unotools/ucbstreamhelper.hxx> 34 #include <unotools/textsearch.hxx> 35 #include <comphelper/sequence.hxx> 36 #include <comphelper/processfactory.hxx> 37 #include <ucbhelper/content.hxx> 38 39 #include <com/sun/star/task/XInteractionApprove.hpp> 40 #include <com/sun/star/task/XInteractionAbort.hpp> 41 #include <com/sun/star/ucb/XCommandInfo.hpp> 42 #include <com/sun/star/ucb/TransferInfo.hpp> 43 #include <com/sun/star/ucb/NameClash.hpp> 44 #include <com/sun/star/ucb/XCommandEnvironment.hpp> 45 #include <com/sun/star/xml/xpath/XXPathAPI.hpp> 46 #include <com/sun/star/beans/NamedValue.hpp> 47 #include <com/sun/star/deployment/ExtensionManager.hpp> 48 49 #include <com/sun/star/deployment/VersionException.hpp> 50 #include <dp_gui_handleversionexception.hxx> 51 #include <com/sun/star/deployment/DeploymentException.hpp> 52 53 using namespace ::com::sun::star; 54 using namespace ::com::sun::star::uno; 55 56 namespace migration 57 { 58 59 static ::rtl::OUString sExtensionSubDir = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/uno_packages/" ) ); 60 static ::rtl::OUString sSubDirName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "cache" ) ); 61 static ::rtl::OUString sConfigDir = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/registry/data" ) ); 62 static ::rtl::OUString sOrgDir = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/registry/data/org" ) ); 63 static ::rtl::OUString sExcludeDir1 = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/registry/data/org" ) ); 64 static ::rtl::OUString sExcludeDir2 = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/registry/data/org/openoffice" ) ); 65 static ::rtl::OUString sDescriptionXmlFile = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/description.xml" ) ); 66 static ::rtl::OUString sExtensionRootSubDirName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/uno_packages" ) ); 67 68 static ::rtl::OUString sConfigurationDataType = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("application/vnd.sun.star.configuration-data")); 69 static ::rtl::OUString sConfigurationSchemaType = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("application/vnd.sun.star.configuration-schema")); 70 71 // ============================================================================= 72 // component operations 73 // ============================================================================= 74 75 ::rtl::OUString OO3ExtensionMigration_getImplementationName() 76 { 77 static ::rtl::OUString* pImplName = 0; 78 if ( !pImplName ) 79 { 80 ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); 81 if ( !pImplName ) 82 { 83 static ::rtl::OUString aImplName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.desktop.migration.OOo3Extensions" ) ); 84 pImplName = &aImplName; 85 } 86 } 87 return *pImplName; 88 } 89 90 // ----------------------------------------------------------------------------- 91 92 Sequence< ::rtl::OUString > OO3ExtensionMigration_getSupportedServiceNames() 93 { 94 static Sequence< ::rtl::OUString >* pNames = 0; 95 if ( !pNames ) 96 { 97 ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); 98 if ( !pNames ) 99 { 100 static Sequence< ::rtl::OUString > aNames(1); 101 aNames.getArray()[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.migration.Extensions" ) ); 102 pNames = &aNames; 103 } 104 } 105 return *pNames; 106 } 107 108 // ============================================================================= 109 // ExtensionMigration 110 // ============================================================================= 111 112 OO3ExtensionMigration::OO3ExtensionMigration(Reference< XComponentContext > const & ctx) : 113 m_ctx(ctx) 114 { 115 } 116 117 // ----------------------------------------------------------------------------- 118 119 OO3ExtensionMigration::~OO3ExtensionMigration() 120 { 121 } 122 123 ::osl::FileBase::RC OO3ExtensionMigration::checkAndCreateDirectory( INetURLObject& rDirURL ) 124 { 125 ::osl::FileBase::RC aResult = ::osl::Directory::create( rDirURL.GetMainURL( INetURLObject::DECODE_TO_IURI ) ); 126 if ( aResult == ::osl::FileBase::E_NOENT ) 127 { 128 INetURLObject aBaseURL( rDirURL ); 129 aBaseURL.removeSegment(); 130 checkAndCreateDirectory( aBaseURL ); 131 return ::osl::Directory::create( rDirURL.GetMainURL( INetURLObject::DECODE_TO_IURI ) ); 132 } 133 else 134 { 135 return aResult; 136 } 137 } 138 139 void OO3ExtensionMigration::scanUserExtensions( const ::rtl::OUString& sSourceDir, TStringVector& aMigrateExtensions ) 140 { 141 osl::Directory aScanRootDir( sSourceDir ); 142 osl::FileStatus fs(FileStatusMask_Type | FileStatusMask_FileURL); 143 osl::FileBase::RC nRetCode = aScanRootDir.open(); 144 if ( nRetCode == osl::Directory::E_None ) 145 { 146 sal_uInt32 nHint( 0 ); 147 osl::DirectoryItem aItem; 148 while ( aScanRootDir.getNextItem( aItem, nHint ) == osl::Directory::E_None ) 149 { 150 if (( aItem.getFileStatus(fs) == osl::FileBase::E_None ) && 151 ( fs.getFileType() == osl::FileStatus::Directory )) 152 { 153 //Check next folder as the "real" extension folder is below a temp folder! 154 ::rtl::OUString sExtensionFolderURL = fs.getFileURL(); 155 156 osl::DirectoryItem aExtDirItem; 157 osl::Directory aExtensionRootDir( sExtensionFolderURL ); 158 159 nRetCode = aExtensionRootDir.open(); 160 if (( nRetCode == osl::Directory::E_None ) && 161 ( aExtensionRootDir.getNextItem( aExtDirItem, nHint ) == osl::Directory::E_None )) 162 { 163 bool bFileStatus = aExtDirItem.getFileStatus(fs) == osl::FileBase::E_None; 164 bool bIsDir = fs.getFileType() == osl::FileStatus::Directory; 165 166 if ( bFileStatus && bIsDir ) 167 { 168 sExtensionFolderURL = fs.getFileURL(); 169 ScanResult eResult = scanExtensionFolder( sExtensionFolderURL ); 170 if ( eResult == SCANRESULT_MIGRATE_EXTENSION ) 171 aMigrateExtensions.push_back( sExtensionFolderURL ); 172 } 173 } 174 } 175 } 176 } 177 } 178 179 OO3ExtensionMigration::ScanResult OO3ExtensionMigration::scanExtensionFolder( const ::rtl::OUString& sExtFolder ) 180 { 181 ScanResult aResult = SCANRESULT_NOTFOUND; 182 osl::Directory aDir(sExtFolder); 183 184 // get sub dirs 185 if (aDir.open() == osl::FileBase::E_None) 186 { 187 // work through directory contents... 188 osl::DirectoryItem item; 189 osl::FileStatus fs(FileStatusMask_Type | FileStatusMask_FileURL); 190 TStringVector aDirectories; 191 while ((aDir.getNextItem(item) == osl::FileBase::E_None ) && 192 ( aResult == SCANRESULT_NOTFOUND )) 193 { 194 if (item.getFileStatus(fs) == osl::FileBase::E_None) 195 { 196 ::rtl::OUString aDirEntryURL; 197 if (fs.getFileType() == osl::FileStatus::Directory) 198 aDirectories.push_back( fs.getFileURL() ); 199 else 200 { 201 aDirEntryURL = fs.getFileURL(); 202 if ( aDirEntryURL.indexOf( sDescriptionXmlFile ) > 0 ) 203 aResult = scanDescriptionXml( aDirEntryURL ) ? SCANRESULT_MIGRATE_EXTENSION : SCANRESULT_DONTMIGRATE_EXTENSION; 204 } 205 } 206 } 207 208 TStringVector::const_iterator pIter = aDirectories.begin(); 209 while ( pIter != aDirectories.end() && aResult == SCANRESULT_NOTFOUND ) 210 { 211 aResult = scanExtensionFolder( *pIter ); 212 ++pIter; 213 } 214 } 215 return aResult; 216 } 217 218 bool OO3ExtensionMigration::scanDescriptionXml( const ::rtl::OUString& sDescriptionXmlURL ) 219 { 220 if ( !m_xDocBuilder.is() ) 221 { 222 m_xDocBuilder = uno::Reference< xml::dom::XDocumentBuilder >( 223 m_ctx->getServiceManager()->createInstanceWithContext( 224 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.xml.dom.DocumentBuilder")), 225 m_ctx ), uno::UNO_QUERY ); 226 } 227 228 if ( !m_xSimpleFileAccess.is() ) 229 { 230 m_xSimpleFileAccess = uno::Reference< ucb::XSimpleFileAccess >( 231 m_ctx->getServiceManager()->createInstanceWithContext( 232 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.ucb.SimpleFileAccess")), 233 m_ctx ), uno::UNO_QUERY ); 234 } 235 236 ::rtl::OUString aExtIdentifier; 237 if ( m_xDocBuilder.is() && m_xSimpleFileAccess.is() ) 238 { 239 try 240 { 241 uno::Reference< io::XInputStream > xIn = 242 m_xSimpleFileAccess->openFileRead( sDescriptionXmlURL ); 243 244 if ( xIn.is() ) 245 { 246 uno::Reference< xml::dom::XDocument > xDoc = m_xDocBuilder->parse( xIn ); 247 if ( xDoc.is() ) 248 { 249 uno::Reference< xml::dom::XElement > xRoot = xDoc->getDocumentElement(); 250 if ( xRoot.is() && 251 xRoot->getTagName().equals(::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("description"))) ) 252 { 253 uno::Reference< xml::xpath::XXPathAPI > xPath( 254 m_ctx->getServiceManager()->createInstanceWithContext( 255 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.xml.xpath.XPathAPI")), 256 m_ctx), 257 uno::UNO_QUERY); 258 259 xPath->registerNS( 260 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc")), 261 xRoot->getNamespaceURI()); 262 xPath->registerNS( 263 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("xlink")), 264 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("http://www.w3.org/1999/xlink"))); 265 266 try 267 { 268 uno::Reference< xml::dom::XNode > xRootNode( xRoot, uno::UNO_QUERY ); 269 uno::Reference< xml::dom::XNode > xNode( 270 xPath->selectSingleNode( 271 xRootNode, 272 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc:identifier/@value")) )); 273 if ( xNode.is() ) 274 aExtIdentifier = xNode->getNodeValue(); 275 } 276 catch ( xml::xpath::XPathException& ) 277 { 278 } 279 catch ( xml::dom::DOMException& ) 280 { 281 } 282 } 283 } 284 } 285 286 if ( aExtIdentifier.getLength() > 0 ) 287 { 288 // scan extension identifier and try to match with our black list entries 289 for ( sal_uInt32 i = 0; i < m_aBlackList.size(); i++ ) 290 { 291 utl::SearchParam param(m_aBlackList[i], utl::SearchParam::SRCH_REGEXP); 292 utl::TextSearch ts(param, LANGUAGE_DONTKNOW); 293 294 xub_StrLen start = 0; 295 xub_StrLen end = static_cast<sal_uInt16>(aExtIdentifier.getLength()); 296 if (ts.SearchFrwrd(aExtIdentifier, &start, &end)) 297 return false; 298 } 299 } 300 } 301 catch ( ucb::CommandAbortedException& ) 302 { 303 } 304 catch ( uno::RuntimeException& ) 305 { 306 } 307 308 if ( aExtIdentifier.getLength() == 0 ) 309 { 310 // Fallback: 311 // Try to use the folder name to match our black list 312 // as some extensions don't provide an identifier in the 313 // description.xml! 314 for ( sal_uInt32 i = 0; i < m_aBlackList.size(); i++ ) 315 { 316 utl::SearchParam param(m_aBlackList[i], utl::SearchParam::SRCH_REGEXP); 317 utl::TextSearch ts(param, LANGUAGE_DONTKNOW); 318 319 xub_StrLen start = 0; 320 xub_StrLen end = static_cast<sal_uInt16>(sDescriptionXmlURL.getLength()); 321 if (ts.SearchFrwrd(sDescriptionXmlURL, &start, &end)) 322 return false; 323 } 324 } 325 } 326 327 return true; 328 } 329 330 bool OO3ExtensionMigration::migrateExtension( const ::rtl::OUString& sSourceDir ) 331 { 332 if ( !m_xExtensionManager.is() ) 333 { 334 try 335 { 336 m_xExtensionManager = deployment::ExtensionManager::get( m_ctx ); 337 } 338 catch ( ucb::CommandFailedException & ){} 339 catch ( uno::RuntimeException & ) {} 340 } 341 342 if ( m_xExtensionManager.is() ) 343 { 344 try 345 { 346 TmpRepositoryCommandEnv* pCmdEnv = new TmpRepositoryCommandEnv(); 347 348 uno::Reference< ucb::XCommandEnvironment > xCmdEnv( 349 static_cast< cppu::OWeakObject* >( pCmdEnv ), uno::UNO_QUERY ); 350 uno::Reference< task::XAbortChannel > xAbortChannel; 351 uno::Reference< deployment::XPackage > xPackage = 352 m_xExtensionManager->addExtension( 353 sSourceDir, uno::Sequence<beans::NamedValue>(), 354 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("user")), xAbortChannel, xCmdEnv ); 355 356 if ( xPackage.is() ) 357 return true; 358 } 359 catch ( ucb::CommandFailedException& ) 360 { 361 } 362 catch ( ucb::CommandAbortedException& ) 363 { 364 } 365 catch ( lang::IllegalArgumentException& ) 366 { 367 } 368 catch ( deployment::DeploymentException& ) 369 { 370 } 371 catch ( uno::RuntimeException & ) 372 { 373 } 374 } 375 376 return false; 377 } 378 379 380 // ----------------------------------------------------------------------------- 381 // XServiceInfo 382 // ----------------------------------------------------------------------------- 383 384 ::rtl::OUString OO3ExtensionMigration::getImplementationName() throw (RuntimeException) 385 { 386 return OO3ExtensionMigration_getImplementationName(); 387 } 388 389 // ----------------------------------------------------------------------------- 390 391 sal_Bool OO3ExtensionMigration::supportsService( const ::rtl::OUString& rServiceName ) throw (RuntimeException) 392 { 393 Sequence< ::rtl::OUString > aNames( getSupportedServiceNames() ); 394 const ::rtl::OUString* pNames = aNames.getConstArray(); 395 const ::rtl::OUString* pEnd = pNames + aNames.getLength(); 396 for ( ; pNames != pEnd && !pNames->equals( rServiceName ); ++pNames ) 397 ; 398 399 return pNames != pEnd; 400 } 401 402 // ----------------------------------------------------------------------------- 403 404 Sequence< ::rtl::OUString > OO3ExtensionMigration::getSupportedServiceNames() throw (RuntimeException) 405 { 406 return OO3ExtensionMigration_getSupportedServiceNames(); 407 } 408 409 // ----------------------------------------------------------------------------- 410 // XInitialization 411 // ----------------------------------------------------------------------------- 412 413 void OO3ExtensionMigration::initialize( const Sequence< Any >& aArguments ) throw (Exception, RuntimeException) 414 { 415 ::osl::MutexGuard aGuard( m_aMutex ); 416 417 const Any* pIter = aArguments.getConstArray(); 418 const Any* pEnd = pIter + aArguments.getLength(); 419 for ( ; pIter != pEnd ; ++pIter ) 420 { 421 beans::NamedValue aValue; 422 *pIter >>= aValue; 423 if ( aValue.Name.equalsAscii( "UserData" ) ) 424 { 425 if ( !(aValue.Value >>= m_sSourceDir) ) 426 { 427 OSL_ENSURE( false, "ExtensionMigration::initialize: argument UserData has wrong type!" ); 428 } 429 } 430 else if ( aValue.Name.equalsAscii( "ExtensionBlackList" ) ) 431 { 432 Sequence< ::rtl::OUString > aBlackList; 433 if ( (aValue.Value >>= aBlackList ) && ( aBlackList.getLength() > 0 )) 434 { 435 m_aBlackList.resize( aBlackList.getLength() ); 436 ::comphelper::sequenceToArray< ::rtl::OUString >( &m_aBlackList[0], aBlackList ); 437 } 438 } 439 } 440 } 441 442 // ----------------------------------------------------------------------------- 443 444 TStringVectorPtr getContent( const ::rtl::OUString& rBaseURL ) 445 { 446 TStringVectorPtr aResult( new TStringVector ); 447 ::osl::Directory aDir( rBaseURL); 448 if ( aDir.open() == ::osl::FileBase::E_None ) 449 { 450 // iterate over directory content 451 TStringVector aSubDirs; 452 ::osl::DirectoryItem aItem; 453 while ( aDir.getNextItem( aItem ) == ::osl::FileBase::E_None ) 454 { 455 ::osl::FileStatus aFileStatus( FileStatusMask_Type | FileStatusMask_FileURL ); 456 if ( aItem.getFileStatus( aFileStatus ) == ::osl::FileBase::E_None ) 457 aResult->push_back( aFileStatus.getFileURL() ); 458 } 459 } 460 461 return aResult; 462 } 463 464 Any OO3ExtensionMigration::execute( const Sequence< beans::NamedValue >& ) 465 throw (lang::IllegalArgumentException, Exception, RuntimeException) 466 { 467 ::osl::MutexGuard aGuard( m_aMutex ); 468 469 ::utl::Bootstrap::PathStatus aStatus = ::utl::Bootstrap::locateUserInstallation( m_sTargetDir ); 470 if ( aStatus == ::utl::Bootstrap::PATH_EXISTS ) 471 { 472 // copy all extensions 473 ::rtl::OUString sSourceDir( m_sSourceDir ); 474 sSourceDir += sExtensionSubDir; 475 sSourceDir += sSubDirName; 476 sSourceDir += sExtensionRootSubDirName; 477 TStringVector aExtensionToMigrate; 478 scanUserExtensions( sSourceDir, aExtensionToMigrate ); 479 if ( aExtensionToMigrate.size() > 0 ) 480 { 481 TStringVector::iterator pIter = aExtensionToMigrate.begin(); 482 while ( pIter != aExtensionToMigrate.end() ) 483 { 484 migrateExtension( *pIter ); 485 ++pIter; 486 } 487 } 488 } 489 490 return Any(); 491 } 492 493 // ----------------------------------------------------------------------------- 494 // TmpRepositoryCommandEnv 495 // ----------------------------------------------------------------------------- 496 497 TmpRepositoryCommandEnv::TmpRepositoryCommandEnv() 498 { 499 } 500 501 TmpRepositoryCommandEnv::~TmpRepositoryCommandEnv() 502 { 503 } 504 // XCommandEnvironment 505 //______________________________________________________________________________ 506 uno::Reference< task::XInteractionHandler > TmpRepositoryCommandEnv::getInteractionHandler() 507 throw ( uno::RuntimeException ) 508 { 509 return this; 510 } 511 512 //______________________________________________________________________________ 513 uno::Reference< ucb::XProgressHandler > TmpRepositoryCommandEnv::getProgressHandler() 514 throw ( uno::RuntimeException ) 515 { 516 return this; 517 } 518 519 // XInteractionHandler 520 void TmpRepositoryCommandEnv::handle( 521 uno::Reference< task::XInteractionRequest> const & xRequest ) 522 throw ( uno::RuntimeException ) 523 { 524 uno::Any request( xRequest->getRequest() ); 525 OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION ); 526 527 bool approve = true; 528 bool abort = false; 529 530 deployment::VersionException verExc; 531 if ( xRequest->getRequest() >>= verExc ) 532 { 533 // user interaction, if an extension is already been installed. 534 approve = handleVersionException( verExc ); 535 abort = !approve; 536 } 537 538 // select: 539 uno::Sequence< Reference< task::XInteractionContinuation > > conts( 540 xRequest->getContinuations() ); 541 Reference< task::XInteractionContinuation > const * pConts = 542 conts.getConstArray(); 543 sal_Int32 len = conts.getLength(); 544 for ( sal_Int32 pos = 0; pos < len; ++pos ) 545 { 546 if (approve) { 547 uno::Reference< task::XInteractionApprove > xInteractionApprove( 548 pConts[ pos ], uno::UNO_QUERY ); 549 if (xInteractionApprove.is()) { 550 xInteractionApprove->select(); 551 // don't query again for ongoing continuations: 552 approve = false; 553 } 554 } 555 else if (abort) { 556 uno::Reference< task::XInteractionAbort > xInteractionAbort( 557 pConts[ pos ], uno::UNO_QUERY ); 558 if (xInteractionAbort.is()) { 559 xInteractionAbort->select(); 560 // don't query again for ongoing continuations: 561 abort = false; 562 } 563 } 564 } 565 } 566 567 // XProgressHandler 568 void TmpRepositoryCommandEnv::push( uno::Any const & /*Status*/ ) 569 throw (uno::RuntimeException) 570 { 571 } 572 573 574 void TmpRepositoryCommandEnv::update( uno::Any const & /*Status */) 575 throw (uno::RuntimeException) 576 { 577 } 578 579 void TmpRepositoryCommandEnv::pop() throw (uno::RuntimeException) 580 { 581 } 582 583 // ============================================================================= 584 // component operations 585 // ============================================================================= 586 587 Reference< XInterface > SAL_CALL OO3ExtensionMigration_create( 588 Reference< XComponentContext > const & ctx ) 589 SAL_THROW( () ) 590 { 591 return static_cast< lang::XTypeProvider * >( new OO3ExtensionMigration( 592 ctx) ); 593 } 594 595 // ----------------------------------------------------------------------------- 596 597 } // namespace migration 598