office-gobmx/desktop/source/migration/services/oo3extensionmigration.cxx
Noel Grandin 9422879a5c loplugin:ostr in desktop
Change-Id: Ib43d1c3a182badddd870bcb8e052ac7fd0a16fc5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/167270
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
2024-05-07 14:41:40 +02:00

408 lines
13 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include "oo3extensionmigration.hxx"
#include <sal/log.hxx>
#include <osl/file.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <unotools/bootstrap.hxx>
#include <unotools/textsearch.hxx>
#include <comphelper/sequence.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <rtl/ref.hxx>
#include <com/sun/star/task/XInteractionApprove.hpp>
#include <com/sun/star/ucb/CommandAbortedException.hpp>
#include <com/sun/star/ucb/SimpleFileAccess.hpp>
#include <com/sun/star/xml/xpath/XPathAPI.hpp>
#include <com/sun/star/xml/xpath/XPathException.hpp>
#include <com/sun/star/xml/dom/DOMException.hpp>
#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/deployment/ExtensionManager.hpp>
#include <com/sun/star/deployment/XExtensionManager.hpp>
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
namespace migration
{
// ExtensionMigration
OO3ExtensionMigration::OO3ExtensionMigration(Reference< XComponentContext > const & ctx) :
m_ctx(ctx)
{
}
OO3ExtensionMigration::~OO3ExtensionMigration()
{
}
void OO3ExtensionMigration::scanUserExtensions( const OUString& sSourceDir, TStringVector& aMigrateExtensions )
{
osl::Directory aScanRootDir( sSourceDir );
osl::FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL);
osl::FileBase::RC nRetCode = aScanRootDir.open();
if ( nRetCode != osl::Directory::E_None )
return;
sal_uInt32 nHint( 0 );
osl::DirectoryItem aItem;
while ( aScanRootDir.getNextItem( aItem, nHint ) == osl::Directory::E_None )
{
if (( aItem.getFileStatus(fs) == osl::FileBase::E_None ) &&
( fs.getFileType() == osl::FileStatus::Directory ))
{
//Check next folder as the "real" extension folder is below a temp folder!
OUString sExtensionFolderURL = fs.getFileURL();
osl::Directory aExtensionRootDir( sExtensionFolderURL );
nRetCode = aExtensionRootDir.open();
if ( nRetCode == osl::Directory::E_None )
{
osl::DirectoryItem aExtDirItem;
while ( aExtensionRootDir.getNextItem( aExtDirItem, nHint ) == osl::Directory::E_None )
{
bool bFileStatus = aExtDirItem.getFileStatus(fs) == osl::FileBase::E_None;
bool bIsDir = fs.getFileType() == osl::FileStatus::Directory;
if ( bFileStatus && bIsDir )
{
sExtensionFolderURL = fs.getFileURL();
ScanResult eResult = scanExtensionFolder( sExtensionFolderURL );
if ( eResult == SCANRESULT_MIGRATE_EXTENSION )
aMigrateExtensions.push_back( sExtensionFolderURL );
break;
}
}
}
}
}
}
OO3ExtensionMigration::ScanResult OO3ExtensionMigration::scanExtensionFolder( const OUString& sExtFolder )
{
ScanResult aResult = SCANRESULT_NOTFOUND;
osl::Directory aDir(sExtFolder);
// get sub dirs
if (aDir.open() == osl::FileBase::E_None)
{
// work through directory contents...
osl::DirectoryItem item;
osl::FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL);
TStringVector aDirectories;
while ((aDir.getNextItem(item) == osl::FileBase::E_None ) &&
( aResult == SCANRESULT_NOTFOUND ))
{
if (item.getFileStatus(fs) == osl::FileBase::E_None)
{
if (fs.getFileType() == osl::FileStatus::Directory)
aDirectories.push_back( fs.getFileURL() );
else
{
OUString aDirEntryURL = fs.getFileURL();
if ( aDirEntryURL.indexOf( "/description.xml" ) > 0 )
aResult = scanDescriptionXml( aDirEntryURL ) ? SCANRESULT_MIGRATE_EXTENSION : SCANRESULT_DONTMIGRATE_EXTENSION;
}
}
}
for (auto const& directory : aDirectories)
{
aResult = scanExtensionFolder(directory);
if (aResult != SCANRESULT_NOTFOUND)
break;
}
}
return aResult;
}
bool OO3ExtensionMigration::scanDescriptionXml( const OUString& sDescriptionXmlURL )
{
if ( !m_xDocBuilder.is() )
{
m_xDocBuilder.set( xml::dom::DocumentBuilder::create(m_ctx) );
}
if ( !m_xSimpleFileAccess.is() )
{
m_xSimpleFileAccess = ucb::SimpleFileAccess::create(m_ctx);
}
OUString aExtIdentifier;
try
{
uno::Reference< io::XInputStream > xIn =
m_xSimpleFileAccess->openFileRead( sDescriptionXmlURL );
if ( xIn.is() )
{
uno::Reference< xml::dom::XDocument > xDoc = m_xDocBuilder->parse( xIn );
if ( xDoc.is() )
{
uno::Reference< xml::dom::XElement > xRoot = xDoc->getDocumentElement();
if ( xRoot.is() && xRoot->getTagName() == "description" )
{
uno::Reference< xml::xpath::XXPathAPI > xPath = xml::xpath::XPathAPI::create(m_ctx);
xPath->registerNS(u"desc"_ustr, xRoot->getNamespaceURI());
xPath->registerNS(u"xlink"_ustr, u"http://www.w3.org/1999/xlink"_ustr);
try
{
uno::Reference< xml::dom::XNode > xNode(
xPath->selectSingleNode(
xRoot, u"desc:identifier/@value"_ustr ));
if ( xNode.is() )
aExtIdentifier = xNode->getNodeValue();
}
catch ( const xml::xpath::XPathException& )
{
}
catch ( const xml::dom::DOMException& )
{
}
}
}
}
if ( !aExtIdentifier.isEmpty() )
{
// scan extension identifier and try to match with our black list entries
for (const OUString & i : m_aDenyList)
{
utl::SearchParam param(i, utl::SearchParam::SearchType::Regexp);
utl::TextSearch ts(param, LANGUAGE_DONTKNOW);
sal_Int32 start = 0;
sal_Int32 end = aExtIdentifier.getLength();
if (ts.SearchForward(aExtIdentifier, &start, &end))
return false;
}
}
}
catch ( const ucb::CommandAbortedException& )
{
}
catch ( const uno::RuntimeException& )
{
}
if ( aExtIdentifier.isEmpty() )
{
// Fallback:
// Try to use the folder name to match our black list
// as some extensions don't provide an identifier in the
// description.xml!
for (const OUString & i : m_aDenyList)
{
utl::SearchParam param(i, utl::SearchParam::SearchType::Regexp);
utl::TextSearch ts(param, LANGUAGE_DONTKNOW);
sal_Int32 start = 0;
sal_Int32 end = sDescriptionXmlURL.getLength();
if (ts.SearchForward(sDescriptionXmlURL, &start, &end))
return false;
}
}
return true;
}
void OO3ExtensionMigration::migrateExtension( const OUString& sSourceDir )
{
css::uno::Reference< css::deployment::XExtensionManager > extMgr(
deployment::ExtensionManager::get( m_ctx ) );
try
{
rtl::Reference<TmpRepositoryCommandEnv> pCmdEnv = new TmpRepositoryCommandEnv();
uno::Reference< task::XAbortChannel > xAbortChannel;
extMgr->addExtension(
sSourceDir, uno::Sequence<beans::NamedValue>(), u"user"_ustr,
xAbortChannel, pCmdEnv );
}
catch ( css::uno::Exception & )
{
TOOLS_WARN_EXCEPTION(
"desktop.migration",
"Ignoring UNO Exception while migrating extension from <" << sSourceDir << ">");
}
}
// XServiceInfo
OUString OO3ExtensionMigration::getImplementationName()
{
return u"com.sun.star.comp.desktop.migration.OOo3Extensions"_ustr;
}
sal_Bool OO3ExtensionMigration::supportsService(OUString const & ServiceName)
{
return cppu::supportsService(this, ServiceName);
}
Sequence< OUString > OO3ExtensionMigration::getSupportedServiceNames()
{
return { u"com.sun.star.migration.Extensions"_ustr };
}
// XInitialization
void OO3ExtensionMigration::initialize( const Sequence< Any >& aArguments )
{
::osl::MutexGuard aGuard( m_aMutex );
const Any* pIter = aArguments.getConstArray();
const Any* pEnd = pIter + aArguments.getLength();
for ( ; pIter != pEnd ; ++pIter )
{
beans::NamedValue aValue;
*pIter >>= aValue;
if ( aValue.Name == "UserData" )
{
if ( !(aValue.Value >>= m_sSourceDir) )
{
OSL_FAIL( "ExtensionMigration::initialize: argument UserData has wrong type!" );
}
}
else if ( aValue.Name == "ExtensionDenyList" )
{
Sequence< OUString > aDenyList;
if ( (aValue.Value >>= aDenyList ) && aDenyList.hasElements())
{
m_aDenyList.resize( aDenyList.getLength() );
::comphelper::sequenceToArray< OUString >( m_aDenyList.data(), aDenyList );
}
}
}
}
Any OO3ExtensionMigration::execute( const Sequence< beans::NamedValue >& )
{
::osl::MutexGuard aGuard( m_aMutex );
::utl::Bootstrap::PathStatus aStatus = ::utl::Bootstrap::locateUserInstallation( m_sTargetDir );
if ( aStatus == ::utl::Bootstrap::PATH_EXISTS )
{
// copy all extensions
OUString sSourceDir = m_sSourceDir +
"/user/uno_packages/cache/uno_packages";
TStringVector aExtensionToMigrate;
scanUserExtensions( sSourceDir, aExtensionToMigrate );
for (auto const& extensionToMigrate : aExtensionToMigrate)
{
migrateExtension(extensionToMigrate);
}
}
return Any();
}
// TmpRepositoryCommandEnv
TmpRepositoryCommandEnv::TmpRepositoryCommandEnv()
{
}
TmpRepositoryCommandEnv::~TmpRepositoryCommandEnv()
{
}
// XCommandEnvironment
uno::Reference< task::XInteractionHandler > TmpRepositoryCommandEnv::getInteractionHandler()
{
return this;
}
uno::Reference< ucb::XProgressHandler > TmpRepositoryCommandEnv::getProgressHandler()
{
return this;
}
// XInteractionHandler
void TmpRepositoryCommandEnv::handle(
uno::Reference< task::XInteractionRequest> const & xRequest )
{
OSL_ASSERT( xRequest->getRequest().getValueTypeClass() == uno::TypeClass_EXCEPTION );
bool approve = true;
// select:
uno::Sequence< Reference< task::XInteractionContinuation > > conts(
xRequest->getContinuations() );
Reference< task::XInteractionContinuation > const * pConts =
conts.getConstArray();
sal_Int32 len = conts.getLength();
for ( sal_Int32 pos = 0; pos < len; ++pos )
{
if (approve) {
uno::Reference< task::XInteractionApprove > xInteractionApprove(
pConts[ pos ], uno::UNO_QUERY );
if (xInteractionApprove.is()) {
xInteractionApprove->select();
// don't query again for ongoing continuations:
approve = false;
}
}
}
}
// XProgressHandler
void TmpRepositoryCommandEnv::push( uno::Any const & /*Status*/ )
{
}
void TmpRepositoryCommandEnv::update( uno::Any const & /*Status */)
{
}
void TmpRepositoryCommandEnv::pop()
{
}
} // namespace migration
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
desktop_OO3ExtensionMigration_get_implementation(
css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
{
return cppu::acquire(new migration::OO3ExtensionMigration(context));
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */