cid#1636693 COPY_INSTEAD_OF_MOVE
[LibreOffice.git] / desktop / source / migration / services / oo3extensionmigration.cxx
blob70835a3cf18761bed632929e2d7c2c4fae7c7d5c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "oo3extensionmigration.hxx"
22 #include <sal/log.hxx>
23 #include <osl/file.hxx>
24 #include <comphelper/diagnose_ex.hxx>
25 #include <unotools/bootstrap.hxx>
26 #include <unotools/textsearch.hxx>
27 #include <comphelper/sequence.hxx>
28 #include <cppuhelper/supportsservice.hxx>
29 #include <rtl/ref.hxx>
31 #include <com/sun/star/task/XInteractionApprove.hpp>
32 #include <com/sun/star/ucb/CommandAbortedException.hpp>
33 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
34 #include <com/sun/star/xml/xpath/XPathAPI.hpp>
35 #include <com/sun/star/xml/xpath/XPathException.hpp>
36 #include <com/sun/star/xml/dom/DOMException.hpp>
37 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
38 #include <com/sun/star/beans/NamedValue.hpp>
39 #include <com/sun/star/deployment/ExtensionManager.hpp>
40 #include <com/sun/star/deployment/XExtensionManager.hpp>
42 using namespace ::com::sun::star;
43 using namespace ::com::sun::star::uno;
45 namespace migration
48 // ExtensionMigration
51 OO3ExtensionMigration::OO3ExtensionMigration(Reference< XComponentContext > const & ctx) :
52 m_ctx(ctx)
57 OO3ExtensionMigration::~OO3ExtensionMigration()
61 void OO3ExtensionMigration::scanUserExtensions( const OUString& sSourceDir, TStringVector& aMigrateExtensions )
63 osl::Directory aScanRootDir( sSourceDir );
64 osl::FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL);
65 osl::FileBase::RC nRetCode = aScanRootDir.open();
66 if ( nRetCode != osl::Directory::E_None )
67 return;
69 sal_uInt32 nHint( 0 );
70 osl::DirectoryItem aItem;
71 while ( aScanRootDir.getNextItem( aItem, nHint ) == osl::Directory::E_None )
73 if (( aItem.getFileStatus(fs) == osl::FileBase::E_None ) &&
74 ( fs.getFileType() == osl::FileStatus::Directory ))
76 //Check next folder as the "real" extension folder is below a temp folder!
77 OUString sExtensionFolderURL = fs.getFileURL();
79 osl::Directory aExtensionRootDir( sExtensionFolderURL );
81 nRetCode = aExtensionRootDir.open();
82 if ( nRetCode == osl::Directory::E_None )
84 osl::DirectoryItem aExtDirItem;
85 while ( aExtensionRootDir.getNextItem( aExtDirItem, nHint ) == osl::Directory::E_None )
87 bool bFileStatus = aExtDirItem.getFileStatus(fs) == osl::FileBase::E_None;
88 bool bIsDir = fs.getFileType() == osl::FileStatus::Directory;
90 if ( bFileStatus && bIsDir )
92 sExtensionFolderURL = fs.getFileURL();
93 ScanResult eResult = scanExtensionFolder( sExtensionFolderURL );
94 if ( eResult == SCANRESULT_MIGRATE_EXTENSION )
95 aMigrateExtensions.push_back( sExtensionFolderURL );
96 break;
104 OO3ExtensionMigration::ScanResult OO3ExtensionMigration::scanExtensionFolder( const OUString& sExtFolder )
106 ScanResult aResult = SCANRESULT_NOTFOUND;
107 osl::Directory aDir(sExtFolder);
109 // get sub dirs
110 if (aDir.open() == osl::FileBase::E_None)
112 // work through directory contents...
113 osl::DirectoryItem item;
114 osl::FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL);
115 TStringVector aDirectories;
116 while ((aDir.getNextItem(item) == osl::FileBase::E_None ) &&
117 ( aResult == SCANRESULT_NOTFOUND ))
119 if (item.getFileStatus(fs) == osl::FileBase::E_None)
121 if (fs.getFileType() == osl::FileStatus::Directory)
122 aDirectories.push_back( fs.getFileURL() );
123 else
125 OUString aDirEntryURL = fs.getFileURL();
126 if ( aDirEntryURL.indexOf( "/description.xml" ) > 0 )
127 aResult = scanDescriptionXml( aDirEntryURL ) ? SCANRESULT_MIGRATE_EXTENSION : SCANRESULT_DONTMIGRATE_EXTENSION;
132 for (auto const& directory : aDirectories)
134 aResult = scanExtensionFolder(directory);
135 if (aResult != SCANRESULT_NOTFOUND)
136 break;
139 return aResult;
142 bool OO3ExtensionMigration::scanDescriptionXml( const OUString& sDescriptionXmlURL )
144 if ( !m_xDocBuilder.is() )
146 m_xDocBuilder.set( xml::dom::DocumentBuilder::create(m_ctx) );
149 if ( !m_xSimpleFileAccess.is() )
151 m_xSimpleFileAccess = ucb::SimpleFileAccess::create(m_ctx);
154 OUString aExtIdentifier;
157 uno::Reference< io::XInputStream > xIn =
158 m_xSimpleFileAccess->openFileRead( sDescriptionXmlURL );
160 if ( xIn.is() )
162 uno::Reference< xml::dom::XDocument > xDoc = m_xDocBuilder->parse( xIn );
163 if ( xDoc.is() )
165 uno::Reference< xml::dom::XElement > xRoot = xDoc->getDocumentElement();
166 if ( xRoot.is() && xRoot->getTagName() == "description" )
168 uno::Reference< xml::xpath::XXPathAPI > xPath = xml::xpath::XPathAPI::create(m_ctx);
170 xPath->registerNS(u"desc"_ustr, xRoot->getNamespaceURI());
171 xPath->registerNS(u"xlink"_ustr, u"http://www.w3.org/1999/xlink"_ustr);
175 uno::Reference< xml::dom::XNode > xNode(
176 xPath->selectSingleNode(
177 xRoot, u"desc:identifier/@value"_ustr ));
178 if ( xNode.is() )
179 aExtIdentifier = xNode->getNodeValue();
181 catch ( const xml::xpath::XPathException& )
184 catch ( const xml::dom::DOMException& )
191 if ( !aExtIdentifier.isEmpty() )
193 // scan extension identifier and try to match with our black list entries
194 for (const OUString & i : m_aDenyList)
196 utl::SearchParam param(i, utl::SearchParam::SearchType::Regexp);
197 utl::TextSearch ts(param, LANGUAGE_DONTKNOW);
199 sal_Int32 start = 0;
200 sal_Int32 end = aExtIdentifier.getLength();
201 if (ts.SearchForward(aExtIdentifier, &start, &end))
202 return false;
206 catch ( const ucb::CommandAbortedException& )
209 catch ( const uno::RuntimeException& )
213 if ( aExtIdentifier.isEmpty() )
215 // Fallback:
216 // Try to use the folder name to match our black list
217 // as some extensions don't provide an identifier in the
218 // description.xml!
219 for (const OUString & i : m_aDenyList)
221 utl::SearchParam param(i, utl::SearchParam::SearchType::Regexp);
222 utl::TextSearch ts(param, LANGUAGE_DONTKNOW);
224 sal_Int32 start = 0;
225 sal_Int32 end = sDescriptionXmlURL.getLength();
226 if (ts.SearchForward(sDescriptionXmlURL, &start, &end))
227 return false;
231 return true;
234 void OO3ExtensionMigration::migrateExtension( const OUString& sSourceDir )
236 css::uno::Reference< css::deployment::XExtensionManager > extMgr(
237 deployment::ExtensionManager::get( m_ctx ) );
240 rtl::Reference<TmpRepositoryCommandEnv> pCmdEnv = new TmpRepositoryCommandEnv();
242 uno::Reference< task::XAbortChannel > xAbortChannel;
243 extMgr->addExtension(
244 sSourceDir, uno::Sequence<beans::NamedValue>(), u"user"_ustr,
245 xAbortChannel, pCmdEnv );
247 catch ( css::uno::Exception & )
249 TOOLS_WARN_EXCEPTION(
250 "desktop.migration",
251 "Ignoring UNO Exception while migrating extension from <" << sSourceDir << ">");
256 // XServiceInfo
259 OUString OO3ExtensionMigration::getImplementationName()
261 return u"com.sun.star.comp.desktop.migration.OOo3Extensions"_ustr;
265 sal_Bool OO3ExtensionMigration::supportsService(OUString const & ServiceName)
267 return cppu::supportsService(this, ServiceName);
271 Sequence< OUString > OO3ExtensionMigration::getSupportedServiceNames()
273 return { u"com.sun.star.migration.Extensions"_ustr };
277 // XInitialization
280 void OO3ExtensionMigration::initialize( const Sequence< Any >& aArguments )
282 ::osl::MutexGuard aGuard( m_aMutex );
284 const Any* pIter = aArguments.getConstArray();
285 const Any* pEnd = pIter + aArguments.getLength();
286 for ( ; pIter != pEnd ; ++pIter )
288 beans::NamedValue aValue;
289 *pIter >>= aValue;
290 if ( aValue.Name == "UserData" )
292 if ( !(aValue.Value >>= m_sSourceDir) )
294 OSL_FAIL( "ExtensionMigration::initialize: argument UserData has wrong type!" );
297 else if ( aValue.Name == "ExtensionDenyList" )
299 Sequence< OUString > aDenyList;
300 if ( (aValue.Value >>= aDenyList ) && aDenyList.hasElements())
302 m_aDenyList.resize( aDenyList.getLength() );
303 ::comphelper::sequenceToArray< OUString >( m_aDenyList.data(), aDenyList );
309 Any OO3ExtensionMigration::execute( const Sequence< beans::NamedValue >& )
311 ::osl::MutexGuard aGuard( m_aMutex );
313 ::utl::Bootstrap::PathStatus aStatus = ::utl::Bootstrap::locateUserInstallation( m_sTargetDir );
314 if ( aStatus == ::utl::Bootstrap::PATH_EXISTS )
316 // copy all extensions
317 OUString sSourceDir = m_sSourceDir +
318 "/user/uno_packages/cache/uno_packages";
319 TStringVector aExtensionToMigrate;
320 scanUserExtensions( sSourceDir, aExtensionToMigrate );
321 for (auto const& extensionToMigrate : aExtensionToMigrate)
323 migrateExtension(extensionToMigrate);
327 return Any();
331 // TmpRepositoryCommandEnv
334 TmpRepositoryCommandEnv::TmpRepositoryCommandEnv()
338 TmpRepositoryCommandEnv::~TmpRepositoryCommandEnv()
341 // XCommandEnvironment
343 uno::Reference< task::XInteractionHandler > TmpRepositoryCommandEnv::getInteractionHandler()
345 return this;
349 uno::Reference< ucb::XProgressHandler > TmpRepositoryCommandEnv::getProgressHandler()
351 return this;
354 // XInteractionHandler
355 void TmpRepositoryCommandEnv::handle(
356 uno::Reference< task::XInteractionRequest> const & xRequest )
358 OSL_ASSERT( xRequest->getRequest().getValueTypeClass() == uno::TypeClass_EXCEPTION );
360 bool approve = true;
362 // select:
363 uno::Sequence< Reference< task::XInteractionContinuation > > conts(
364 xRequest->getContinuations() );
365 Reference< task::XInteractionContinuation > const * pConts =
366 conts.getConstArray();
367 sal_Int32 len = conts.getLength();
368 for ( sal_Int32 pos = 0; pos < len; ++pos )
370 if (approve) {
371 uno::Reference< task::XInteractionApprove > xInteractionApprove(
372 pConts[ pos ], uno::UNO_QUERY );
373 if (xInteractionApprove.is()) {
374 xInteractionApprove->select();
375 // don't query again for ongoing continuations:
376 approve = false;
382 // XProgressHandler
383 void TmpRepositoryCommandEnv::push( uno::Any const & /*Status*/ )
388 void TmpRepositoryCommandEnv::update( uno::Any const & /*Status */)
392 void TmpRepositoryCommandEnv::pop()
397 } // namespace migration
400 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
401 desktop_OO3ExtensionMigration_get_implementation(
402 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
404 return cppu::acquire(new migration::OO3ExtensionMigration(context));
408 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */