1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <rtl/instance.hxx>
23 #include <osl/file.hxx>
24 #include <osl/thread.h>
25 #include <tools/urlobj.hxx>
26 #include <unotools/bootstrap.hxx>
27 #include <unotools/ucbstreamhelper.hxx>
28 #include <unotools/textsearch.hxx>
29 #include <comphelper/sequence.hxx>
30 #include <comphelper/processfactory.hxx>
31 #include <cppuhelper/supportsservice.hxx>
32 #include <ucbhelper/content.hxx>
34 #include <com/sun/star/task/XInteractionApprove.hpp>
35 #include <com/sun/star/task/XInteractionAbort.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/dom/DocumentBuilder.hpp>
42 #include <com/sun/star/beans/NamedValue.hpp>
43 #include <com/sun/star/deployment/ExtensionManager.hpp>
44 #include <com/sun/star/deployment/XExtensionManager.hpp>
46 using namespace ::com::sun::star
;
47 using namespace ::com::sun::star::uno
;
52 static OUString
sExtensionSubDir( "/user/uno_packages/" );
53 static OUString
sSubDirName( "cache" );
54 static OUString
sDescriptionXmlFile( "/description.xml" );
55 static OUString
sExtensionRootSubDirName( "/uno_packages" );
57 // =============================================================================
58 // component operations
59 // =============================================================================
61 OUString
OO3ExtensionMigration_getImplementationName()
63 static OUString
* pImplName
= 0;
66 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
69 static OUString
aImplName( "com.sun.star.comp.desktop.migration.OOo3Extensions" );
70 pImplName
= &aImplName
;
76 // -----------------------------------------------------------------------------
78 Sequence
< OUString
> OO3ExtensionMigration_getSupportedServiceNames()
80 static Sequence
< OUString
>* pNames
= 0;
83 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
86 static Sequence
< OUString
> aNames(1);
87 aNames
.getArray()[0] = "com.sun.star.migration.Extensions";
94 // =============================================================================
96 // =============================================================================
98 OO3ExtensionMigration::OO3ExtensionMigration(Reference
< XComponentContext
> const & ctx
) :
103 // -----------------------------------------------------------------------------
105 OO3ExtensionMigration::~OO3ExtensionMigration()
109 ::osl::FileBase::RC
OO3ExtensionMigration::checkAndCreateDirectory( INetURLObject
& rDirURL
)
111 ::osl::FileBase::RC aResult
= ::osl::Directory::create( rDirURL
.GetMainURL( INetURLObject::DECODE_TO_IURI
) );
112 if ( aResult
== ::osl::FileBase::E_NOENT
)
114 INetURLObject
aBaseURL( rDirURL
);
115 aBaseURL
.removeSegment();
116 checkAndCreateDirectory( aBaseURL
);
117 return ::osl::Directory::create( rDirURL
.GetMainURL( INetURLObject::DECODE_TO_IURI
) );
125 void OO3ExtensionMigration::scanUserExtensions( const OUString
& sSourceDir
, TStringVector
& aMigrateExtensions
)
127 osl::Directory
aScanRootDir( sSourceDir
);
128 osl::FileStatus
fs(osl_FileStatus_Mask_Type
| osl_FileStatus_Mask_FileURL
);
129 osl::FileBase::RC nRetCode
= aScanRootDir
.open();
130 if ( nRetCode
== osl::Directory::E_None
)
132 sal_uInt32
nHint( 0 );
133 osl::DirectoryItem aItem
;
134 while ( aScanRootDir
.getNextItem( aItem
, nHint
) == osl::Directory::E_None
)
136 if (( aItem
.getFileStatus(fs
) == osl::FileBase::E_None
) &&
137 ( fs
.getFileType() == osl::FileStatus::Directory
))
139 //Check next folder as the "real" extension folder is below a temp folder!
140 OUString sExtensionFolderURL
= fs
.getFileURL();
142 osl::Directory
aExtensionRootDir( sExtensionFolderURL
);
144 nRetCode
= aExtensionRootDir
.open();
145 if ( nRetCode
== osl::Directory::E_None
)
147 osl::DirectoryItem aExtDirItem
;
148 while ( aExtensionRootDir
.getNextItem( aExtDirItem
, nHint
) == osl::Directory::E_None
)
150 bool bFileStatus
= aExtDirItem
.getFileStatus(fs
) == osl::FileBase::E_None
;
151 bool bIsDir
= fs
.getFileType() == osl::FileStatus::Directory
;
153 if ( bFileStatus
&& bIsDir
)
155 sExtensionFolderURL
= fs
.getFileURL();
156 ScanResult eResult
= scanExtensionFolder( sExtensionFolderURL
);
157 if ( eResult
== SCANRESULT_MIGRATE_EXTENSION
)
158 aMigrateExtensions
.push_back( sExtensionFolderURL
);
168 OO3ExtensionMigration::ScanResult
OO3ExtensionMigration::scanExtensionFolder( const OUString
& sExtFolder
)
170 ScanResult aResult
= SCANRESULT_NOTFOUND
;
171 osl::Directory
aDir(sExtFolder
);
174 if (aDir
.open() == osl::FileBase::E_None
)
176 // work through directory contents...
177 osl::DirectoryItem item
;
178 osl::FileStatus
fs(osl_FileStatus_Mask_Type
| osl_FileStatus_Mask_FileURL
);
179 TStringVector aDirectories
;
180 while ((aDir
.getNextItem(item
) == osl::FileBase::E_None
) &&
181 ( aResult
== SCANRESULT_NOTFOUND
))
183 if (item
.getFileStatus(fs
) == osl::FileBase::E_None
)
185 OUString aDirEntryURL
;
186 if (fs
.getFileType() == osl::FileStatus::Directory
)
187 aDirectories
.push_back( fs
.getFileURL() );
190 aDirEntryURL
= fs
.getFileURL();
191 if ( aDirEntryURL
.indexOf( sDescriptionXmlFile
) > 0 )
192 aResult
= scanDescriptionXml( aDirEntryURL
) ? SCANRESULT_MIGRATE_EXTENSION
: SCANRESULT_DONTMIGRATE_EXTENSION
;
197 TStringVector::const_iterator pIter
= aDirectories
.begin();
198 while ( pIter
!= aDirectories
.end() && aResult
== SCANRESULT_NOTFOUND
)
200 aResult
= scanExtensionFolder( *pIter
);
207 bool OO3ExtensionMigration::scanDescriptionXml( const OUString
& sDescriptionXmlURL
)
209 if ( !m_xDocBuilder
.is() )
211 m_xDocBuilder
= uno::Reference
< xml::dom::XDocumentBuilder
>( xml::dom::DocumentBuilder::create(m_ctx
) );
214 if ( !m_xSimpleFileAccess
.is() )
216 m_xSimpleFileAccess
= ucb::SimpleFileAccess::create(m_ctx
);
219 OUString aExtIdentifier
;
222 uno::Reference
< io::XInputStream
> xIn
=
223 m_xSimpleFileAccess
->openFileRead( sDescriptionXmlURL
);
227 uno::Reference
< xml::dom::XDocument
> xDoc
= m_xDocBuilder
->parse( xIn
);
230 uno::Reference
< xml::dom::XElement
> xRoot
= xDoc
->getDocumentElement();
231 if ( xRoot
.is() && xRoot
->getTagName() == "description" )
233 uno::Reference
< xml::xpath::XXPathAPI
> xPath
= xml::xpath::XPathAPI::create(m_ctx
);
235 xPath
->registerNS("desc", xRoot
->getNamespaceURI());
236 xPath
->registerNS("xlink", "http://www.w3.org/1999/xlink");
240 uno::Reference
< xml::dom::XNode
> xRootNode( xRoot
, uno::UNO_QUERY
);
241 uno::Reference
< xml::dom::XNode
> xNode(
242 xPath
->selectSingleNode(
243 xRootNode
, "desc:identifier/@value" ));
245 aExtIdentifier
= xNode
->getNodeValue();
247 catch ( const xml::xpath::XPathException
& )
250 catch ( const xml::dom::DOMException
& )
257 if ( !aExtIdentifier
.isEmpty() )
259 // scan extension identifier and try to match with our black list entries
260 for ( sal_uInt32 i
= 0; i
< m_aBlackList
.size(); i
++ )
262 utl::SearchParam
param(m_aBlackList
[i
], utl::SearchParam::SRCH_REGEXP
);
263 utl::TextSearch
ts(param
, LANGUAGE_DONTKNOW
);
266 sal_Int32 end
= aExtIdentifier
.getLength();
267 if (ts
.SearchForward(aExtIdentifier
, &start
, &end
))
272 catch ( const ucb::CommandAbortedException
& )
275 catch ( const uno::RuntimeException
& )
279 if ( aExtIdentifier
.isEmpty() )
282 // Try to use the folder name to match our black list
283 // as some extensions don't provide an identifier in the
285 for ( sal_uInt32 i
= 0; i
< m_aBlackList
.size(); i
++ )
287 utl::SearchParam
param(m_aBlackList
[i
], utl::SearchParam::SRCH_REGEXP
);
288 utl::TextSearch
ts(param
, LANGUAGE_DONTKNOW
);
291 sal_Int32 end
= sDescriptionXmlURL
.getLength();
292 if (ts
.SearchForward(sDescriptionXmlURL
, &start
, &end
))
300 void OO3ExtensionMigration::migrateExtension( const OUString
& sSourceDir
)
302 css::uno::Reference
< css::deployment::XExtensionManager
> extMgr(
303 deployment::ExtensionManager::get( m_ctx
) );
306 TmpRepositoryCommandEnv
* pCmdEnv
= new TmpRepositoryCommandEnv();
308 uno::Reference
< ucb::XCommandEnvironment
> xCmdEnv(
309 static_cast< cppu::OWeakObject
* >( pCmdEnv
), uno::UNO_QUERY
);
310 uno::Reference
< task::XAbortChannel
> xAbortChannel
;
311 extMgr
->addExtension(
312 sSourceDir
, uno::Sequence
<beans::NamedValue
>(), "user",
313 xAbortChannel
, xCmdEnv
);
315 catch ( css::uno::Exception
& e
)
319 "Ignoring UNO Exception while migrating extension from <"
320 << sSourceDir
<< ">: \"" << e
.Message
<< "\"");
325 // -----------------------------------------------------------------------------
327 // -----------------------------------------------------------------------------
329 OUString
OO3ExtensionMigration::getImplementationName() throw (RuntimeException
)
331 return OO3ExtensionMigration_getImplementationName();
334 // -----------------------------------------------------------------------------
336 sal_Bool
OO3ExtensionMigration::supportsService(OUString
const & ServiceName
)
337 throw (css::uno::RuntimeException
)
339 return cppu::supportsService(this, ServiceName
);
342 // -----------------------------------------------------------------------------
344 Sequence
< OUString
> OO3ExtensionMigration::getSupportedServiceNames() throw (RuntimeException
)
346 return OO3ExtensionMigration_getSupportedServiceNames();
349 // -----------------------------------------------------------------------------
351 // -----------------------------------------------------------------------------
353 void OO3ExtensionMigration::initialize( const Sequence
< Any
>& aArguments
) throw (Exception
, RuntimeException
)
355 ::osl::MutexGuard
aGuard( m_aMutex
);
357 const Any
* pIter
= aArguments
.getConstArray();
358 const Any
* pEnd
= pIter
+ aArguments
.getLength();
359 for ( ; pIter
!= pEnd
; ++pIter
)
361 beans::NamedValue aValue
;
363 if ( aValue
.Name
== "UserData" )
365 if ( !(aValue
.Value
>>= m_sSourceDir
) )
367 OSL_FAIL( "ExtensionMigration::initialize: argument UserData has wrong type!" );
370 else if ( aValue
.Name
== "ExtensionBlackList" )
372 Sequence
< OUString
> aBlackList
;
373 if ( (aValue
.Value
>>= aBlackList
) && ( aBlackList
.getLength() > 0 ))
375 m_aBlackList
.resize( aBlackList
.getLength() );
376 ::comphelper::sequenceToArray
< OUString
>( &m_aBlackList
[0], aBlackList
);
382 Any
OO3ExtensionMigration::execute( const Sequence
< beans::NamedValue
>& )
383 throw (lang::IllegalArgumentException
, Exception
, RuntimeException
)
385 ::osl::MutexGuard
aGuard( m_aMutex
);
387 ::utl::Bootstrap::PathStatus aStatus
= ::utl::Bootstrap::locateUserInstallation( m_sTargetDir
);
388 if ( aStatus
== ::utl::Bootstrap::PATH_EXISTS
)
390 // copy all extensions
391 OUString
sSourceDir( m_sSourceDir
);
392 sSourceDir
+= sExtensionSubDir
;
393 sSourceDir
+= sSubDirName
;
394 sSourceDir
+= sExtensionRootSubDirName
;
395 TStringVector aExtensionToMigrate
;
396 scanUserExtensions( sSourceDir
, aExtensionToMigrate
);
397 if ( aExtensionToMigrate
.size() > 0 )
399 TStringVector::iterator pIter
= aExtensionToMigrate
.begin();
400 while ( pIter
!= aExtensionToMigrate
.end() )
402 migrateExtension( *pIter
);
411 // -----------------------------------------------------------------------------
412 // TmpRepositoryCommandEnv
413 // -----------------------------------------------------------------------------
415 TmpRepositoryCommandEnv::TmpRepositoryCommandEnv()
419 TmpRepositoryCommandEnv::~TmpRepositoryCommandEnv()
422 // XCommandEnvironment
423 //______________________________________________________________________________
424 uno::Reference
< task::XInteractionHandler
> TmpRepositoryCommandEnv::getInteractionHandler()
425 throw ( uno::RuntimeException
)
430 //______________________________________________________________________________
431 uno::Reference
< ucb::XProgressHandler
> TmpRepositoryCommandEnv::getProgressHandler()
432 throw ( uno::RuntimeException
)
437 // XInteractionHandler
438 void TmpRepositoryCommandEnv::handle(
439 uno::Reference
< task::XInteractionRequest
> const & xRequest
)
440 throw ( uno::RuntimeException
)
442 OSL_ASSERT( xRequest
->getRequest().getValueTypeClass() == uno::TypeClass_EXCEPTION
);
448 uno::Sequence
< Reference
< task::XInteractionContinuation
> > conts(
449 xRequest
->getContinuations() );
450 Reference
< task::XInteractionContinuation
> const * pConts
=
451 conts
.getConstArray();
452 sal_Int32 len
= conts
.getLength();
453 for ( sal_Int32 pos
= 0; pos
< len
; ++pos
)
456 uno::Reference
< task::XInteractionApprove
> xInteractionApprove(
457 pConts
[ pos
], uno::UNO_QUERY
);
458 if (xInteractionApprove
.is()) {
459 xInteractionApprove
->select();
460 // don't query again for ongoing continuations:
465 uno::Reference
< task::XInteractionAbort
> xInteractionAbort(
466 pConts
[ pos
], uno::UNO_QUERY
);
467 if (xInteractionAbort
.is()) {
468 xInteractionAbort
->select();
469 // don't query again for ongoing continuations:
477 void TmpRepositoryCommandEnv::push( uno::Any
const & /*Status*/ )
478 throw (uno::RuntimeException
)
483 void TmpRepositoryCommandEnv::update( uno::Any
const & /*Status */)
484 throw (uno::RuntimeException
)
488 void TmpRepositoryCommandEnv::pop() throw (uno::RuntimeException
)
492 // =============================================================================
493 // component operations
494 // =============================================================================
496 Reference
< XInterface
> SAL_CALL
OO3ExtensionMigration_create(
497 Reference
< XComponentContext
> const & ctx
)
500 return static_cast< lang::XTypeProvider
* >( new OO3ExtensionMigration(
504 // -----------------------------------------------------------------------------
506 } // namespace migration
508 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */