LibreOffice Module desktop (master)  1
oo3extensionmigration.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
22 #include <rtl/instance.hxx>
23 #include <sal/log.hxx>
24 #include <osl/file.hxx>
25 #include <osl/thread.h>
26 #include <tools/urlobj.hxx>
27 #include <unotools/bootstrap.hxx>
28 #include <unotools/textsearch.hxx>
29 #include <comphelper/sequence.hxx>
31 #include <ucbhelper/content.hxx>
32 
33 #include <com/sun/star/task/XInteractionApprove.hpp>
34 #include <com/sun/star/task/XInteractionAbort.hpp>
35 #include <com/sun/star/ucb/CommandAbortedException.hpp>
36 #include <com/sun/star/ucb/XCommandInfo.hpp>
37 #include <com/sun/star/ucb/TransferInfo.hpp>
38 #include <com/sun/star/ucb/NameClash.hpp>
39 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
40 #include <com/sun/star/xml/xpath/XPathAPI.hpp>
41 #include <com/sun/star/xml/xpath/XPathException.hpp>
42 #include <com/sun/star/xml/dom/DOMException.hpp>
43 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
44 #include <com/sun/star/beans/NamedValue.hpp>
45 #include <com/sun/star/deployment/ExtensionManager.hpp>
46 #include <com/sun/star/deployment/XExtensionManager.hpp>
47 
48 using namespace ::com::sun::star;
49 using namespace ::com::sun::star::uno;
50 
51 namespace migration
52 {
53 
54 // component operations
55 
56 
58 {
59  return OUString( "com.sun.star.comp.desktop.migration.OOo3Extensions" );
60 }
61 
62 
64 {
65  return { "com.sun.star.migration.Extensions" };
66 }
67 
68 
69 // ExtensionMigration
70 
71 
73 m_ctx(ctx)
74 {
75 }
76 
77 
79 {
80 }
81 
82 void OO3ExtensionMigration::scanUserExtensions( const OUString& sSourceDir, TStringVector& aMigrateExtensions )
83 {
84  osl::Directory aScanRootDir( sSourceDir );
85  osl::FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL);
86  osl::FileBase::RC nRetCode = aScanRootDir.open();
87  if ( nRetCode == osl::Directory::E_None )
88  {
89  sal_uInt32 nHint( 0 );
90  osl::DirectoryItem aItem;
91  while ( aScanRootDir.getNextItem( aItem, nHint ) == osl::Directory::E_None )
92  {
93  if (( aItem.getFileStatus(fs) == osl::FileBase::E_None ) &&
94  ( fs.getFileType() == osl::FileStatus::Directory ))
95  {
96  //Check next folder as the "real" extension folder is below a temp folder!
97  OUString sExtensionFolderURL = fs.getFileURL();
98 
99  osl::Directory aExtensionRootDir( sExtensionFolderURL );
100 
101  nRetCode = aExtensionRootDir.open();
102  if ( nRetCode == osl::Directory::E_None )
103  {
104  osl::DirectoryItem aExtDirItem;
105  while ( aExtensionRootDir.getNextItem( aExtDirItem, nHint ) == osl::Directory::E_None )
106  {
107  bool bFileStatus = aExtDirItem.getFileStatus(fs) == osl::FileBase::E_None;
108  bool bIsDir = fs.getFileType() == osl::FileStatus::Directory;
109 
110  if ( bFileStatus && bIsDir )
111  {
112  sExtensionFolderURL = fs.getFileURL();
113  ScanResult eResult = scanExtensionFolder( sExtensionFolderURL );
114  if ( eResult == SCANRESULT_MIGRATE_EXTENSION )
115  aMigrateExtensions.push_back( sExtensionFolderURL );
116  break;
117  }
118  }
119  }
120  }
121  }
122  }
123 }
124 
126 {
128  osl::Directory aDir(sExtFolder);
129 
130  // get sub dirs
131  if (aDir.open() == osl::FileBase::E_None)
132  {
133  // work through directory contents...
134  osl::DirectoryItem item;
135  osl::FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL);
136  TStringVector aDirectories;
137  while ((aDir.getNextItem(item) == osl::FileBase::E_None ) &&
138  ( aResult == SCANRESULT_NOTFOUND ))
139  {
140  if (item.getFileStatus(fs) == osl::FileBase::E_None)
141  {
142  OUString aDirEntryURL;
143  if (fs.getFileType() == osl::FileStatus::Directory)
144  aDirectories.push_back( fs.getFileURL() );
145  else
146  {
147  aDirEntryURL = fs.getFileURL();
148  if ( aDirEntryURL.indexOf( "/description.xml" ) > 0 )
150  }
151  }
152  }
153 
154  for (auto const& directory : aDirectories)
155  {
156  aResult = scanExtensionFolder(directory);
157  if (aResult != SCANRESULT_NOTFOUND)
158  break;
159  }
160  }
161  return aResult;
162 }
163 
164 bool OO3ExtensionMigration::scanDescriptionXml( const OUString& sDescriptionXmlURL )
165 {
166  if ( !m_xDocBuilder.is() )
167  {
169  }
170 
171  if ( !m_xSimpleFileAccess.is() )
172  {
174  }
175 
176  OUString aExtIdentifier;
177  try
178  {
179  uno::Reference< io::XInputStream > xIn =
180  m_xSimpleFileAccess->openFileRead( sDescriptionXmlURL );
181 
182  if ( xIn.is() )
183  {
184  uno::Reference< xml::dom::XDocument > xDoc = m_xDocBuilder->parse( xIn );
185  if ( xDoc.is() )
186  {
187  uno::Reference< xml::dom::XElement > xRoot = xDoc->getDocumentElement();
188  if ( xRoot.is() && xRoot->getTagName() == "description" )
189  {
190  uno::Reference< xml::xpath::XXPathAPI > xPath = xml::xpath::XPathAPI::create(m_ctx);
191 
192  xPath->registerNS("desc", xRoot->getNamespaceURI());
193  xPath->registerNS("xlink", "http://www.w3.org/1999/xlink");
194 
195  try
196  {
197  uno::Reference< xml::dom::XNode > xRootNode( xRoot, uno::UNO_QUERY );
198  uno::Reference< xml::dom::XNode > xNode(
199  xPath->selectSingleNode(
200  xRootNode, "desc:identifier/@value" ));
201  if ( xNode.is() )
202  aExtIdentifier = xNode->getNodeValue();
203  }
204  catch ( const xml::xpath::XPathException& )
205  {
206  }
207  catch ( const xml::dom::DOMException& )
208  {
209  }
210  }
211  }
212  }
213 
214  if ( !aExtIdentifier.isEmpty() )
215  {
216  // scan extension identifier and try to match with our black list entries
217  for (OUString & i : m_aBlackList)
218  {
221 
222  sal_Int32 start = 0;
223  sal_Int32 end = aExtIdentifier.getLength();
224  if (ts.SearchForward(aExtIdentifier, &start, &end))
225  return false;
226  }
227  }
228  }
229  catch ( const ucb::CommandAbortedException& )
230  {
231  }
232  catch ( const uno::RuntimeException& )
233  {
234  }
235 
236  if ( aExtIdentifier.isEmpty() )
237  {
238  // Fallback:
239  // Try to use the folder name to match our black list
240  // as some extensions don't provide an identifier in the
241  // description.xml!
242  for (OUString & i : m_aBlackList)
243  {
246 
247  sal_Int32 start = 0;
248  sal_Int32 end = sDescriptionXmlURL.getLength();
249  if (ts.SearchForward(sDescriptionXmlURL, &start, &end))
250  return false;
251  }
252  }
253 
254  return true;
255 }
256 
257 void OO3ExtensionMigration::migrateExtension( const OUString& sSourceDir )
258 {
259  css::uno::Reference< css::deployment::XExtensionManager > extMgr(
260  deployment::ExtensionManager::get( m_ctx ) );
261  try
262  {
264 
265  uno::Reference< ucb::XCommandEnvironment > xCmdEnv(
266  static_cast< cppu::OWeakObject* >( pCmdEnv ), uno::UNO_QUERY );
267  uno::Reference< task::XAbortChannel > xAbortChannel;
268  extMgr->addExtension(
269  sSourceDir, uno::Sequence<beans::NamedValue>(), "user",
270  xAbortChannel, xCmdEnv );
271  }
272  catch ( css::uno::Exception & e )
273  {
274  SAL_WARN(
275  "desktop.migration",
276  "Ignoring UNO Exception while migrating extension from <"
277  << sSourceDir << ">: " << e);
278  }
279 }
280 
281 
282 // XServiceInfo
283 
284 
286 {
288 }
289 
290 
292 {
293  return cppu::supportsService(this, ServiceName);
294 }
295 
296 
298 {
300 }
301 
302 
303 // XInitialization
304 
305 
306 void OO3ExtensionMigration::initialize( const Sequence< Any >& aArguments )
307 {
308  ::osl::MutexGuard aGuard( m_aMutex );
309 
310  const Any* pIter = aArguments.getConstArray();
311  const Any* pEnd = pIter + aArguments.getLength();
312  for ( ; pIter != pEnd ; ++pIter )
313  {
314  beans::NamedValue aValue;
315  *pIter >>= aValue;
316  if ( aValue.Name == "UserData" )
317  {
318  if ( !(aValue.Value >>= m_sSourceDir) )
319  {
320  OSL_FAIL( "ExtensionMigration::initialize: argument UserData has wrong type!" );
321  }
322  }
323  else if ( aValue.Name == "ExtensionBlackList" )
324  {
325  Sequence< OUString > aBlackList;
326  if ( (aValue.Value >>= aBlackList ) && ( aBlackList.getLength() > 0 ))
327  {
328  m_aBlackList.resize( aBlackList.getLength() );
329  ::comphelper::sequenceToArray< OUString >( &m_aBlackList[0], aBlackList );
330  }
331  }
332  }
333 }
334 
335 Any OO3ExtensionMigration::execute( const Sequence< beans::NamedValue >& )
336 {
337  ::osl::MutexGuard aGuard( m_aMutex );
338 
340  if ( aStatus == ::utl::Bootstrap::PATH_EXISTS )
341  {
342  // copy all extensions
343  OUString sSourceDir( m_sSourceDir );
344  sSourceDir += "/user/uno_packages/cache/uno_packages";
345  TStringVector aExtensionToMigrate;
346  scanUserExtensions( sSourceDir, aExtensionToMigrate );
347  for (auto const& extensionToMigrate : aExtensionToMigrate)
348  {
349  migrateExtension(extensionToMigrate);
350  }
351  }
352 
353  return Any();
354 }
355 
356 
357 // TmpRepositoryCommandEnv
358 
359 
361 {
362 }
363 
365 {
366 }
367 // XCommandEnvironment
368 
369 uno::Reference< task::XInteractionHandler > TmpRepositoryCommandEnv::getInteractionHandler()
370 {
371  return this;
372 }
373 
374 
375 uno::Reference< ucb::XProgressHandler > TmpRepositoryCommandEnv::getProgressHandler()
376 {
377  return this;
378 }
379 
380 // XInteractionHandler
382  uno::Reference< task::XInteractionRequest> const & xRequest )
383 {
384  OSL_ASSERT( xRequest->getRequest().getValueTypeClass() == uno::TypeClass_EXCEPTION );
385 
386  bool approve = true;
387 
388  // select:
389  uno::Sequence< Reference< task::XInteractionContinuation > > conts(
390  xRequest->getContinuations() );
391  Reference< task::XInteractionContinuation > const * pConts =
392  conts.getConstArray();
393  sal_Int32 len = conts.getLength();
394  for ( sal_Int32 pos = 0; pos < len; ++pos )
395  {
396  if (approve) {
397  uno::Reference< task::XInteractionApprove > xInteractionApprove(
398  pConts[ pos ], uno::UNO_QUERY );
399  if (xInteractionApprove.is()) {
400  xInteractionApprove->select();
401  // don't query again for ongoing continuations:
402  approve = false;
403  }
404  }
405  }
406 }
407 
408 // XProgressHandler
409 void TmpRepositoryCommandEnv::push( uno::Any const & /*Status*/ )
410 {
411 }
412 
413 
414 void TmpRepositoryCommandEnv::update( uno::Any const & /*Status */)
415 {
416 }
417 
419 {
420 }
421 
422 
423 // component operations
424 
425 
427  Reference< XComponentContext > const & ctx )
428 {
429  return static_cast< lang::XTypeProvider * >( new OO3ExtensionMigration(
430  ctx) );
431 }
432 
433 
434 } // namespace migration
435 
436 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static PathStatus locateUserInstallation(OUString &_rURL)
Reference< XInterface > OO3ExtensionMigration_create(Reference< XComponentContext > const &ctx)
bool scanDescriptionXml(const OUString &sDescriptionXmlFilePath)
OUString OO3ExtensionMigration_getImplementationName()
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > &aArguments) override
virtual void SAL_CALL update(css::uno::Any const &Status) override
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
virtual css::uno::Any SAL_CALL execute(const css::uno::Sequence< css::beans::NamedValue > &Arguments) override
bool SearchForward(const OUString &rStr, sal_Int32 *pStart, sal_Int32 *pEnd, css::util::SearchResult *pRes=nullptr)
virtual css::uno::Reference< css::ucb::XProgressHandler > SAL_CALL getProgressHandler() override
virtual void SAL_CALL push(css::uno::Any const &Status) override
int i
ScanResult scanExtensionFolder(const OUString &sExtFolder)
css::uno::Reference< css::xml::dom::XDocumentBuilder > m_xDocBuilder
virtual sal_Bool SAL_CALL supportsService(const OUString &rServiceName) override
Sequence< OUString > OO3ExtensionMigration_getSupportedServiceNames()
unsigned char sal_Bool
#define LANGUAGE_DONTKNOW
virtual void SAL_CALL pop() override
enumrange< T >::Iterator end(enumrange< T >)
void migrateExtension(const OUString &sSourceDir)
std::vector< OUString > TStringVector
Definition: misc.hxx:33
virtual void SAL_CALL handle(css::uno::Reference< css::task::XInteractionRequest > const &xRequest) override
virtual css::uno::Reference< css::task::XInteractionHandler > SAL_CALL getInteractionHandler() override
css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xSimpleFileAccess
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
#define SAL_WARN(area, stream)
css::uno::Reference< css::uno::XComponentContext > m_ctx
void scanUserExtensions(const OUString &sSourceDir, TStringVector &aMigrateExtensions)
virtual OUString SAL_CALL getImplementationName() override
OO3ExtensionMigration(css::uno::Reference< css::uno::XComponentContext > const &ctx)