1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_desktop.hxx" 30 31 #include "oo3extensionmigration.hxx" 32 #include <rtl/instance.hxx> 33 #include <osl/file.hxx> 34 #include <osl/thread.h> 35 #include <tools/urlobj.hxx> 36 #include <unotools/bootstrap.hxx> 37 #include <unotools/ucbstreamhelper.hxx> 38 #include <unotools/textsearch.hxx> 39 #include <comphelper/sequence.hxx> 40 #include <comphelper/processfactory.hxx> 41 #include <ucbhelper/content.hxx> 42 43 #include <com/sun/star/task/XInteractionApprove.hpp> 44 #include <com/sun/star/task/XInteractionAbort.hpp> 45 #include <com/sun/star/ucb/XCommandInfo.hpp> 46 #include <com/sun/star/ucb/TransferInfo.hpp> 47 #include <com/sun/star/ucb/NameClash.hpp> 48 #include <com/sun/star/ucb/XCommandEnvironment.hpp> 49 #include <com/sun/star/xml/xpath/XXPathAPI.hpp> 50 #include <com/sun/star/beans/NamedValue.hpp> 51 #include <com/sun/star/deployment/ExtensionManager.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 } 369 370 return false; 371 } 372 373 374 // ----------------------------------------------------------------------------- 375 // XServiceInfo 376 // ----------------------------------------------------------------------------- 377 378 ::rtl::OUString OO3ExtensionMigration::getImplementationName() throw (RuntimeException) 379 { 380 return OO3ExtensionMigration_getImplementationName(); 381 } 382 383 // ----------------------------------------------------------------------------- 384 385 sal_Bool OO3ExtensionMigration::supportsService( const ::rtl::OUString& rServiceName ) throw (RuntimeException) 386 { 387 Sequence< ::rtl::OUString > aNames( getSupportedServiceNames() ); 388 const ::rtl::OUString* pNames = aNames.getConstArray(); 389 const ::rtl::OUString* pEnd = pNames + aNames.getLength(); 390 for ( ; pNames != pEnd && !pNames->equals( rServiceName ); ++pNames ) 391 ; 392 393 return pNames != pEnd; 394 } 395 396 // ----------------------------------------------------------------------------- 397 398 Sequence< ::rtl::OUString > OO3ExtensionMigration::getSupportedServiceNames() throw (RuntimeException) 399 { 400 return OO3ExtensionMigration_getSupportedServiceNames(); 401 } 402 403 // ----------------------------------------------------------------------------- 404 // XInitialization 405 // ----------------------------------------------------------------------------- 406 407 void OO3ExtensionMigration::initialize( const Sequence< Any >& aArguments ) throw (Exception, RuntimeException) 408 { 409 ::osl::MutexGuard aGuard( m_aMutex ); 410 411 const Any* pIter = aArguments.getConstArray(); 412 const Any* pEnd = pIter + aArguments.getLength(); 413 for ( ; pIter != pEnd ; ++pIter ) 414 { 415 beans::NamedValue aValue; 416 *pIter >>= aValue; 417 if ( aValue.Name.equalsAscii( "UserData" ) ) 418 { 419 if ( !(aValue.Value >>= m_sSourceDir) ) 420 { 421 OSL_ENSURE( false, "ExtensionMigration::initialize: argument UserData has wrong type!" ); 422 } 423 } 424 else if ( aValue.Name.equalsAscii( "ExtensionBlackList" ) ) 425 { 426 Sequence< ::rtl::OUString > aBlackList; 427 if ( (aValue.Value >>= aBlackList ) && ( aBlackList.getLength() > 0 )) 428 { 429 m_aBlackList.resize( aBlackList.getLength() ); 430 ::comphelper::sequenceToArray< ::rtl::OUString >( &m_aBlackList[0], aBlackList ); 431 } 432 } 433 } 434 } 435 436 // ----------------------------------------------------------------------------- 437 438 TStringVectorPtr getContent( const ::rtl::OUString& rBaseURL ) 439 { 440 TStringVectorPtr aResult( new TStringVector ); 441 ::osl::Directory aDir( rBaseURL); 442 if ( aDir.open() == ::osl::FileBase::E_None ) 443 { 444 // iterate over directory content 445 TStringVector aSubDirs; 446 ::osl::DirectoryItem aItem; 447 while ( aDir.getNextItem( aItem ) == ::osl::FileBase::E_None ) 448 { 449 ::osl::FileStatus aFileStatus( FileStatusMask_Type | FileStatusMask_FileURL ); 450 if ( aItem.getFileStatus( aFileStatus ) == ::osl::FileBase::E_None ) 451 aResult->push_back( aFileStatus.getFileURL() ); 452 } 453 } 454 455 return aResult; 456 } 457 458 Any OO3ExtensionMigration::execute( const Sequence< beans::NamedValue >& ) 459 throw (lang::IllegalArgumentException, Exception, RuntimeException) 460 { 461 ::osl::MutexGuard aGuard( m_aMutex ); 462 463 ::utl::Bootstrap::PathStatus aStatus = ::utl::Bootstrap::locateUserInstallation( m_sTargetDir ); 464 if ( aStatus == ::utl::Bootstrap::PATH_EXISTS ) 465 { 466 // copy all extensions 467 ::rtl::OUString sSourceDir( m_sSourceDir ); 468 sSourceDir += sExtensionSubDir; 469 sSourceDir += sSubDirName; 470 sSourceDir += sExtensionRootSubDirName; 471 TStringVector aExtensionToMigrate; 472 scanUserExtensions( sSourceDir, aExtensionToMigrate ); 473 if ( aExtensionToMigrate.size() > 0 ) 474 { 475 TStringVector::iterator pIter = aExtensionToMigrate.begin(); 476 while ( pIter != aExtensionToMigrate.end() ) 477 { 478 migrateExtension( *pIter ); 479 ++pIter; 480 } 481 } 482 } 483 484 return Any(); 485 } 486 487 // ----------------------------------------------------------------------------- 488 // TmpRepositoryCommandEnv 489 // ----------------------------------------------------------------------------- 490 491 TmpRepositoryCommandEnv::TmpRepositoryCommandEnv() 492 { 493 } 494 495 TmpRepositoryCommandEnv::~TmpRepositoryCommandEnv() 496 { 497 } 498 // XCommandEnvironment 499 //______________________________________________________________________________ 500 uno::Reference< task::XInteractionHandler > TmpRepositoryCommandEnv::getInteractionHandler() 501 throw ( uno::RuntimeException ) 502 { 503 return this; 504 } 505 506 //______________________________________________________________________________ 507 uno::Reference< ucb::XProgressHandler > TmpRepositoryCommandEnv::getProgressHandler() 508 throw ( uno::RuntimeException ) 509 { 510 return this; 511 } 512 513 // XInteractionHandler 514 void TmpRepositoryCommandEnv::handle( 515 uno::Reference< task::XInteractionRequest> const & xRequest ) 516 throw ( uno::RuntimeException ) 517 { 518 uno::Any request( xRequest->getRequest() ); 519 OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION ); 520 521 bool approve = true; 522 bool abort = false; 523 524 // select: 525 uno::Sequence< Reference< task::XInteractionContinuation > > conts( 526 xRequest->getContinuations() ); 527 Reference< task::XInteractionContinuation > const * pConts = 528 conts.getConstArray(); 529 sal_Int32 len = conts.getLength(); 530 for ( sal_Int32 pos = 0; pos < len; ++pos ) 531 { 532 if (approve) { 533 uno::Reference< task::XInteractionApprove > xInteractionApprove( 534 pConts[ pos ], uno::UNO_QUERY ); 535 if (xInteractionApprove.is()) { 536 xInteractionApprove->select(); 537 // don't query again for ongoing continuations: 538 approve = false; 539 } 540 } 541 else if (abort) { 542 uno::Reference< task::XInteractionAbort > xInteractionAbort( 543 pConts[ pos ], uno::UNO_QUERY ); 544 if (xInteractionAbort.is()) { 545 xInteractionAbort->select(); 546 // don't query again for ongoing continuations: 547 abort = false; 548 } 549 } 550 } 551 } 552 553 // XProgressHandler 554 void TmpRepositoryCommandEnv::push( uno::Any const & /*Status*/ ) 555 throw (uno::RuntimeException) 556 { 557 } 558 559 560 void TmpRepositoryCommandEnv::update( uno::Any const & /*Status */) 561 throw (uno::RuntimeException) 562 { 563 } 564 565 void TmpRepositoryCommandEnv::pop() throw (uno::RuntimeException) 566 { 567 } 568 569 // ============================================================================= 570 // component operations 571 // ============================================================================= 572 573 Reference< XInterface > SAL_CALL OO3ExtensionMigration_create( 574 Reference< XComponentContext > const & ctx ) 575 SAL_THROW( () ) 576 { 577 return static_cast< lang::XTypeProvider * >( new OO3ExtensionMigration( 578 ctx) ); 579 } 580 581 // ----------------------------------------------------------------------------- 582 583 } // namespace migration 584