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