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