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