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