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