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